<?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: Fabio</title>
    <description>The latest articles on DEV Community by Fabio (@snakepy).</description>
    <link>https://dev.to/snakepy</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%2F871638%2Fba3db519-a072-48df-99b0-bf0722903331.jpg</url>
      <title>DEV Community: Fabio</title>
      <link>https://dev.to/snakepy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/snakepy"/>
    <language>en</language>
    <item>
      <title>#1 Code Challenge - Chain Of Responsibility - Python</title>
      <dc:creator>Fabio</dc:creator>
      <pubDate>Thu, 19 Jan 2023 22:22:26 +0000</pubDate>
      <link>https://dev.to/snakepy/1-code-challenge-chain-of-responsibility-python-376</link>
      <guid>https://dev.to/snakepy/1-code-challenge-chain-of-responsibility-python-376</guid>
      <description>&lt;p&gt;&lt;b&gt;In my code challenges series, I am tackling code challenges and try to transform them into real world issue. With that Approach I can then build a small application to illustrate the problem better. Checkout the base &lt;a href="https://github.com/snake-py/practical-coding-challenges" rel="noopener noreferrer"&gt;Repository&lt;/a&gt;.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;A support (or any other customer relationship) department usually has multiple levels of expertise or responsibility. This is the perfect place to illustrate the Chain of Responsibility pattern. I took this code challenge from a recent interview I had.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Imagine you have a sales department with three levels of employees: backoffice, account manager, and director. Whenever a case is automatically created, it must be first allocated to a backoffice employee who is free. If the backoffice employee can't handle the case, he or she must escalate the case to an account manager. If the manager is not free or not able to handle it, then the case should be escalated to a director.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Theory of Chain of Responsibility
&lt;/h2&gt;

&lt;p&gt;I think there are many better ways to explain the Chain of Responsibility pattern than I can do. However, I will give a very brief overview of wht it actually is.&lt;/p&gt;

&lt;p&gt;Definition: (&lt;a href="https://refactoring.guru/design-patterns/chain-of-responsibility" rel="noopener noreferrer"&gt;source&lt;/a&gt;)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Chain of Responsibility pattern is a behavioral design pattern that allows you to pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our particular case the chain of responsibility is the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backoffice&lt;/li&gt;
&lt;li&gt;Account Manager&lt;/li&gt;
&lt;li&gt;Director&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every employee can decide for them selfs if they can handle the case or not. If they can't handle it, they pass it to the next employee in the chain. If they can handle it, they do so and the chain is broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  End Product
&lt;/h2&gt;

&lt;p&gt;I created a minimal Flask app which accepts cases and then on a second thread I have running a self written queue system. So nothing production ready, but it illustrates the problem. I will try to walk you through the app in the following sections. The App only has two views. You can find the code on &lt;a href="https://github.com/snake-py/practical-coding-challenges/tree/main/%231%20Chain%20of%20Responsebility" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

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

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

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;

&lt;p&gt;I think it is always helpful to understand the entry point of an application, which is inside &lt;a href="https://github.com/snake-py/practical-coding-challenges/blob/main/%231%20Chain%20of%20Responsebility/app.py" rel="noopener noreferrer"&gt;app.py&lt;/a&gt;. There the Flask app is created, routes are registered and the second thread is started on which the TicketSystem Class is running.&lt;/p&gt;

&lt;p&gt;Here is the app schematically represented:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Core Logic
&lt;/h3&gt;

&lt;p&gt;The heart of the application is the &lt;a href="https://github.com/snake-py/practical-coding-challenges/blob/main/%231%20Chain%20of%20Responsebility/modules/Tickets/TicketSystem.py" rel="noopener noreferrer"&gt;TicketSystem Class&lt;/a&gt;. This class is running on a second thread and is responsible for handling the tickets. In combination with the &lt;a href="https://github.com/snake-py/practical-coding-challenges/blob/main/%231%20Chain%20of%20Responsebility/modules/Employee/Employee.py" rel="noopener noreferrer"&gt;Employee classes&lt;/a&gt; the Chain of Responsibility pattern illustrated.&lt;/p&gt;

&lt;p&gt;Let us First look at the TicketSystem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TicketSystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Constructor
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;back_office_employees&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;account_managers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;directors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tickets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;back_office_employees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;back_office_employees&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account_managers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account_managers&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;directors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;directors&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;back_office_employees&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Core Logic
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        This method is running on a second thread and is responsible for handling the tickets.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tickets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;ticket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tickets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Keyboard interrupt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;exit&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        This method is called by the Flask app and adds the ticket to the queue.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tickets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assign_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        This method is responsible for assigning the ticket to the right employee. Called by the work method on the second thread.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_employee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_employee_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_employee_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Employee callback to inform the TicketSystem about the status of the ticket and whether it needs to be put back into the queue.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ticket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;was handled by&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;with status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tickets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_employee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;difficulty&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_free_back_office_employee&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;difficulty&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_free_account_manager_or_director&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;difficulty&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_free_director&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Start of Helpers
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_free_back_office_employee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;back_office_employees&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_free_account_manager_or_director&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account_managers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;directors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_free_director&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;directors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what is happening here? The Flask End point calls the &lt;code&gt;dispatch_case&lt;/code&gt; method which adds the ticket to the queue (&lt;code&gt;self.tickets&lt;/code&gt;). The &lt;code&gt;work&lt;/code&gt; method is running on a second thread and is responsible for handling the tickets. It calls the &lt;code&gt;assign_case&lt;/code&gt; method which uses the helper &lt;code&gt;get_employee&lt;/code&gt;. This method is responsible for finding the right employee for the ticket. So here we can see our chain. If the &lt;code&gt;difficulty&lt;/code&gt; of the ticket is 1, we are looking for a free back office employee. If the &lt;code&gt;difficulty&lt;/code&gt; is 2 or 3, we are looking for a free account manager or director. If the &lt;code&gt;difficulty&lt;/code&gt; is 3, we are looking for a free director.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;difficulty&lt;/code&gt; is set directly on the &lt;a href="https://dev.toLINK"&gt;Ticket&lt;/a&gt; class and can be raised by the employee. Below you can see the call to the &lt;code&gt;assign_case&lt;/code&gt; method of the employee. So we give the employee the ticket and a callback function which.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assign_case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        This method is called by the TicketSystem and set the employee instance up to be blocked and starts the work on the ticket.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle_ticket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_ticket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Here we can see the simulated work of an employee on a ticket.
        (In a real system this would be an employee working on a ticket and either closing it or escalating it)
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_handle_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;difficulty&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculate_sleep_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="c1"&gt;## I will later speak how I simulated the escalation
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_handle_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_difficulty&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escalate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see the employee either raises the difficulty of the ticket or closes it. If the ticket was closed we return &lt;code&gt;True&lt;/code&gt; and the ticket stays removed from queue. If the ticket was escalated we return &lt;code&gt;False&lt;/code&gt; and the ticket is put back into the queue, inside the callback function &lt;code&gt;TicketSystem.handle_employee_response&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To simulate the required level of expertise on a ticket I simply have two difficulties set on a ticket. The &lt;code&gt;target_difficulty&lt;/code&gt; is the required level of expertise and the &lt;code&gt;difficulty&lt;/code&gt; is the current level of expertise. If the employee escalates the ticket, then the &lt;code&gt;difficulty&lt;/code&gt; is raised by one. This will happening till the &lt;code&gt;difficulty&lt;/code&gt; is equal to the &lt;code&gt;target_difficulty&lt;/code&gt; and an employee can handle the ticket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.toLINK"&gt;Ticket&lt;/a&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_difficulty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;target_difficulty&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;target_difficulty&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Difficulty must be between 1 and 3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;difficulty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# 1 = easy, 2 = medium, 3 = hard
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_difficulty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_difficulty&lt;/span&gt; &lt;span class="c1"&gt;# back office employee difficulty target
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TicketStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OPEN&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_employee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  I always love to engage with readers 🚀
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Do you need help, with anything written above?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Do you think I made a good example?&lt;/strong&gt; 😄&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Do you think I can improve?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Did you like the article?&lt;/strong&gt; 🔥&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>5 Things that make you a good developer</title>
      <dc:creator>Fabio</dc:creator>
      <pubDate>Mon, 03 Oct 2022 10:35:51 +0000</pubDate>
      <link>https://dev.to/snakepy/5-things-that-make-you-a-good-programmer-38dd</link>
      <guid>https://dev.to/snakepy/5-things-that-make-you-a-good-programmer-38dd</guid>
      <description>&lt;p&gt;&lt;strong&gt;In all honesty, I find it very pretentious of myself that I write this article. However, just today I read an article on medium which had the same topic, and it was just so wrong in my opinion. What the other article confused were different concepts, and it valued experience more over problem-solving skills and motivation. So I thought I will drop my opinion here. In this article I will not tell you that you need to love what you do or be motivated, I take that as given if you opened this article!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this even matter? 😎😉😋
&lt;/h2&gt;

&lt;p&gt;Programmers are often proud of their work, because very often we solve a problem for the first time in our life. This makes us proud of ourselves and of the work we have delivered. Due to self reflection, we then usually want to compare ourselves to others to get a sense for our "standing". Many people would argue that being proud is imporatant to be a good programmer, my personal opinion is that you need to be proud of your work to even have the motivation to be good at your work (not only for programming).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But being proud of our code can actually be also in our way!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Not open for criticism on our new solution
&lt;/h2&gt;

&lt;p&gt;If we have solved a very hard problem for the first time, guess what chances are that there are much better solutions out there. However, we have put our time and effort in writing this (&lt;em&gt;cough&lt;/em&gt; &lt;strong&gt;peace of garbage&lt;/strong&gt;) implementation. So if now a colleague walks up to us after we have submitted our PR and starts to directly give "good advice", it is important to take a deep breath 👼 stay open to their ideas. Even if it is your first year junior! Being open to the opinion of others lets you improve drastically and learn a lot. If you have valid arguments against their ideas, it is a moment where you can teach your colleagues. So either way, a person will probably learn something. However, if you are just going to ignore their comments, then you will lose the opportunity to improve or teach. So be open on your PR and your code will become much better. None is a god programmer and if you think you are you might wanna change your attitude.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To improve this ability it is often simple enough to tell yourself "to be open" before you go through PR comments.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Being able to read others people code 🌜🌛
&lt;/h2&gt;

&lt;p&gt;I often see people implement a feature twice or even more often, because they are not able to properly read others people code and use their implementation. So, it comes easier to them to implement the feature from scratch or pull in a new library instead of working out how the code base handled functionality like this before. Or sometimes the current feature is not 100% what they need, so instead of refactoring the current solution to make it more generic and reusable for even future features, they fall back into the bad habit of pulling everything off by themselves. Many of us have started to code alone, without any team, so when people then get started they actually never have read complex implementations from other people. &lt;/p&gt;

&lt;p&gt;I mean it is fair enough, often a project has a certain style (or worse no style 😷 😵) and every developer has a certain style as well. Hence, big companies have often such rigid code guidelines. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To improve this simply take more often the harder path and read the other's code properly on a feature request and try to reuse what is there and if required properly refactor it. Often you can also simply see who has committed this piece of code and maybe asked for some initial guidance. I promise if you are going to do this, you will see drastic improvements in your productivity and quality.&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Grasping the bigger picture 🌌🌌🌌
&lt;/h2&gt;

&lt;p&gt;Nowadays, applications are growing more complex, so it is often hard to understand every detail of an app. However, I think it is crucial to understand the bigger picture. To understand the bigger picture I think it is important that you categorize the "products" or services your company is offering into three sections. &lt;/p&gt;

&lt;p&gt;Services and the level of understanding a good programmer has to them. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Directly work with this service 💻&lt;/li&gt;
&lt;li&gt;Require Data from this service 📞&lt;/li&gt;
&lt;li&gt;One or more Service hops away from my working area 📡&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(1) A service on which you are directly working on should become very well known with time. So even if the app is so big that you cannot know all parts of it in detail, at least you should be able to tell where the start of an API is and what the expected outputs are. This helps you to understand the application flow and something like your request or event life cycles. Once you have this internalized, it will become much easier to debug the overall application. Because you know where to start and how to climb down the logic. &lt;/p&gt;

&lt;p&gt;(2) If you are just requiring data of this service, you should grasp the overall workflow, like how is authentication set up, so I can use the service or how is the overall API Schema to quickly evaluate newly added or reworked APIs. Often APIs are being added or reworked and &lt;strong&gt;a particular someone has forgotten to update the god damn documentation! Again!!! 😡😡😡&lt;/strong&gt; But if you know how to deal with the API (and given it is a half way decent API 😅) you are likely to figure out what has changed. &lt;/p&gt;

&lt;p&gt;(3) If you work at such a big company that you have a microservice structure like &lt;a href="https://www.youtube.com/watch?v=CZ3wIuvmHeM"&gt;Netflix&lt;/a&gt;, then it becomes very hard to exactly know how every API is working. But this brings me back, to knowing the overall picture. What you can easily know is what does this service (inputs and outputs). Once you know what all services do, you can often draw a picture in your mind (or literally draw it if it helps you) to understand what the overall flow is. This helps you to understand if for instance a level 2 service is breaking because it requires data from a level 3 service in the wrong way. Then you can much more effectively create an issue report. Better issue reports are als being valued by your colleagues (FYI 😉). &lt;/p&gt;

&lt;h2&gt;
  
  
  4. Use the provided tooling 😩😩😩
&lt;/h2&gt;

&lt;p&gt;I think it is crucial to understand what tooling your company uses and at least how it can benefit you as a developer. I see more than often, developers ignoring tools that their company is providing to them. Tools which have been build or are being paid much money for to improve the developer experience. Why do they ignore it? I think often it is that they are not interested in learning this, because &lt;em&gt;it works like they are doing it&lt;/em&gt; currently. Often they cannot see the benefit it will bring them, and only see the effort they need to invest in order to learn the tool. And in all honesty, who can blame them? We developers are having a lot on our mind and are constantly learning. But to be more productive it is important to use the tooling which is provided and if it does not suit you should ask to improve the tooling. A great developer will have their tools ready to use. And if you can share these with others, even better. &lt;/p&gt;

&lt;p&gt;Let me give an example here, maybe your company use Axiom or Elastic for logging http requests. Then it should become second nature to utilize this for your debugging process. Or you have well-made docker container which ships with a debugger? Then stop using prints and use the debugger. Ah and did I mention that a debugger is awesome. I wrote btw an &lt;a href="https://dev.to/snakepy/my-favorite-laravel-development-environment-with-docker-nginx-php-fpm-xdebug-in-vscode-2o03"&gt;article&lt;/a&gt; on how to set up Laravel in docker with a debugger you might want to check that out. 😄  &lt;/p&gt;

&lt;p&gt;Even more importantly start using a debugger!!! 😒😒😒  &lt;/p&gt;

&lt;p&gt;It is unimaginable, how often I have solved an issue within a few minutes, where my colleagues have struggled for a long time, simple due to the fact that they used prints instead of a debugger. &lt;/p&gt;

&lt;p&gt;Debugger = King ⚡⚡⚡&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Putting in the effort to learn the frameworks your company is using
&lt;/h2&gt;

&lt;p&gt;I think this is something which is often true for smaller companies or some freelancer, which often work on a tighter belt. Frameworks are there to help you, &lt;strong&gt;so take that help and stop to implement your own shitty solution&lt;/strong&gt;. If a part of it is not perfect suited for your needs, you might want to simply extend the framework. But in order to do that, you need to understand the framework.&lt;br&gt;
I know I know, Frameworks have opinions, and you may or may not agree with them. However, if you are using a framework, please try to stick to the framework's opinion as closely as possible. Probably smart people have thought something of these opinions 😛. &lt;/p&gt;

&lt;p&gt;If you are not feeling very strong in a particular framework or library, but you will need to use it, please spend the time to learn at least the basics. I think if you are going to be open to your lead or boss that you will need 2 to 4 hours learning a framework which you should extensively use, then almost none will decline your request. If they will you might wanna look for a better learning enviornment. 🔎🔎🔎&lt;/p&gt;

&lt;h2&gt;
  
  
  LET ME KNOW 🚀🚀🚀
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What is your top 5?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did you like the article?&lt;/strong&gt; 🔥🔥🔥&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I appreciate any feedback&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Cypress and Page Object Pattern - Good practice for EndToEnd testing</title>
      <dc:creator>Fabio</dc:creator>
      <pubDate>Sat, 30 Jul 2022 08:52:00 +0000</pubDate>
      <link>https://dev.to/snakepy/cypress-and-page-object-pattern-good-practice-for-endtoend-testing-16cm</link>
      <guid>https://dev.to/snakepy/cypress-and-page-object-pattern-good-practice-for-endtoend-testing-16cm</guid>
      <description>&lt;p&gt;&lt;strong&gt;In this article, I will show you how you can adapt the Page Object Pattern in conjunction with a naming convention to save you a ton of time while writing your tests. First we start off with a naming convention and then how we can use Page Object Pattern to our advantage.&lt;/strong&gt;  &lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have a look at the end result of the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be consistent with the naming of your Attribute &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is pretty much a no-brainer, but becomes very important the bigger the project is. If you utilize your naming properly, you will be able to abstract your tests in a very neat way. For instance, you can build selectors based on your component names and actions. What I often do is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"registration-button"&lt;/span&gt;
&lt;span class="na"&gt;data-test=&lt;/span&gt;&lt;span class="s"&gt;"registration-page__button-start"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Register&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"open-settings"&lt;/span&gt;
&lt;span class="na"&gt;data-test=&lt;/span&gt;&lt;span class="s"&gt;"settings-page__button-open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Settings&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;so I follow these conventions all the time, depending on the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// it is just important that you are consistent with it and have a system in place
&lt;span class="nt"&gt;&amp;lt;page-name&amp;gt;&lt;/span&gt;__&lt;span class="nt"&gt;&amp;lt;element-type&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;semantic-action&amp;gt;&lt;/span&gt;
or even
&lt;span class="nt"&gt;&amp;lt;page-name&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;component-name&amp;gt;&lt;/span&gt;__&lt;span class="nt"&gt;&amp;lt;element-type&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;semantic-action&amp;gt;&lt;/span&gt;
or just
&lt;span class="nt"&gt;&amp;lt;component-name&amp;gt;&lt;/span&gt;__&lt;span class="nt"&gt;&amp;lt;element-type&amp;gt;&lt;/span&gt;-&lt;span class="nt"&gt;&amp;lt;semantic-action&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This helps me to unify selectors and I do not even need to look 👀 at my markup anymore. The first example button is starting the registration process, hence &lt;code&gt;start&lt;/code&gt;, the second button is opening a widget/components, hence &lt;code&gt;open&lt;/code&gt;. I would recommend that you take the time and define your semantic action and properly communicate them with your team. Good testing starts with a solid naming convention. &lt;/p&gt;

&lt;p&gt;If you do this you can save so much time, have a look at the next section where I show how you can utilize this even more. &lt;/p&gt;

&lt;h2&gt;
  
  
  Split up the test into a reusable Structure
&lt;/h2&gt;

&lt;p&gt;So, with everything in coding once you start to copy your code around you might better off writing a function &lt;em&gt;(I mean fair enough if you just copy one logic from one place to another, and you are sure you will not need it more often than that.)&lt;/em&gt; Splitting up your code into semantic logical chunks makes a lot of sense, for maintainability. And if you are going to spend your time writing end-to-end tests, you will spend 10x more on it if you have a huge project, which does not share any logic. &lt;/p&gt;

&lt;p&gt;There are two main patterns I have come across and which make more or less sense for certain apps. One is the &lt;strong&gt;Page Object Pattern&lt;/strong&gt; and the other is the &lt;strong&gt;Function Object Pattern&lt;/strong&gt; (at least I call it like this 😅).  However, in this article I just show the Page Object Pattern, let me know if you are interested in a more function based approach. 😎&lt;/p&gt;

&lt;h3&gt;
  
  
  Page Object Pattern
&lt;/h3&gt;

&lt;p&gt;Okay, so the Page Object Pattern is a way of semantically grouping page elements into classes. It is often frowned up on, because it can add a layer of complexity to a smaller project. However, classes do have their space, espacially if the project gets big. Let me show you how I utilize classes in a way to save &lt;strong&gt;you&lt;/strong&gt; a ton of time and improve readability. &lt;/p&gt;

&lt;p&gt;Let's say we have two components, which represent both a form, both can be opened and both have submitted buttons and error messages. So, without having seen the component, we already can picture what the test will need to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Component (if it is closed) or simply scroll to it&lt;/li&gt;
&lt;li&gt;fill out the required input with false data&lt;/li&gt;
&lt;li&gt;submit and evaluate if error message(s) are displayed&lt;/li&gt;
&lt;li&gt;fill out with correct data&lt;/li&gt;
&lt;li&gt;submit and evaluate the success message(s)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In fact, pretty much all form components do this! So we can abstract all of it into a base class like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * The Widget Test Interaction Page Object - abstract class!!
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;FormPageObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultOpenSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSubmitUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSubmitButtonSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultInterceptName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSelectorForStringInputChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultCloseSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultMessageSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="cm"&gt;/**
   * Opens the widget
   */&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultOpenSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;inputSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputSelector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&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="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="cm"&gt;/**
   * Close the  widget
   */&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultCloseSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;inputSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputSelector&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="cm"&gt;/**
   * The intercept will register with the default name and url and will look for the next request
   * hence you can wait for it. By default it is expecting that the response will work.
   * You can also force a success true
   */&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;interceptSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;forceSuccess&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;forceFailure&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;shouldSucceed&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;inputUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;interceptName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultInterceptName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputUrl&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultSubmitUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forceSuccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forceFailure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;shouldSucceed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;It was not possible to change the the widget and it was expected that it would be possible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interceptName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;updateStringInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultSelectorForStringInputChange&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultSubmitButtonSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;inputSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputSelector&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;submitValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messageSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultMessageSelector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRequestValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;correctClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert-success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert-danger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkForClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;correctClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;messageSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="s2"&gt;`The submit request returned &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, but the message had not this class &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;correctClass&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&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;I abstracted the code and used some of my own helper methods like &lt;code&gt;cy.$&lt;/code&gt; or &lt;code&gt;cy.stringInput&lt;/code&gt;. &lt;em&gt;(If you are interested how I build my helpers methods let me know in the comments)&lt;/em&gt; The good thing about this abstract class is that now we can use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;FormComponentA&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;FormPageObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultOpenSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentA__button--open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSubmitUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/regexForSubmitUrl/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSubmitButtonSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentA__button--submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultInterceptName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formComponentASubmit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSelectorForStringInputChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentA__input-text--type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultMessageSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentA__message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;FormComponentB&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;FormPageObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultOpenSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentB__button--open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSubmitUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/regexForSubmitUrl/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSubmitButtonSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentB__button--submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultInterceptName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formComponentBSubmit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultSelectorForStringInputChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentB__input-text--type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;defaultMessageSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentB__message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="c1"&gt;// this component needs some custom interaction, e.g. a slider&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;useSlider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;percentage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default-selector-for-slider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moveSlider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&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;JumpToNamingConvention&lt;/p&gt;

&lt;p&gt;Now we can write the actual test logic like this &lt;a&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-for-component-A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should fail and display error message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormComponentA&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;
    &lt;span class="c1"&gt;// e.g. update name input&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateStringInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Max&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; 
    &lt;span class="c1"&gt;// set up the interceptor and force it to fail&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; 
    &lt;span class="c1"&gt;// hit the submit button&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;
    &lt;span class="c1"&gt;// wait for the request and evaluate the response&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultInterceptName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submitValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;



&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should succeed and display success message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormComponentA&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// set up the interceptor and force it to succeed&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateStringInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Max&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultInterceptName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submitValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&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;Our tests are now very good readable 😇. I think anyone can now understand what the test is actually doing. &lt;strong&gt;Readable code is the first step of creating maintainable code!&lt;/strong&gt; The cool thing is, if we want to create a test for &lt;code&gt;FormComponentB&lt;/code&gt; we can very quickly do so! &lt;/p&gt;

&lt;p&gt;Coming back to my earlier statement&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should use a solid naming convention for your tests attributes can really help you utilize the power of this approach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because you can also do stuff like this in your base class(es):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interceptRegex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultOpenSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;__button-open`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultSubmitUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;interceptRegex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultSubmitButtonSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;__button--submit`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultInterceptName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Intercept`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultSelectorForStringInputChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;__input-text--type`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultMessageSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-message`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now you do not even need to write a new class and can use the abstract &lt;code&gt;FormClass&lt;/code&gt; directly. 🔥🔥🔥&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormPageObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-componentB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JumpToTop.&lt;/p&gt;

&lt;h2&gt;
  
  
  LET ME KNOW 🚀🚀🚀
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do you need help, with anything written above?&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What will be your first Image?&lt;/strong&gt; 😄&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you think I can improve - then let me know&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did you like the article?&lt;/strong&gt; 🔥&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>cypress</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Publish your own Docker Images with GitHub Actions</title>
      <dc:creator>Fabio</dc:creator>
      <pubDate>Sun, 24 Jul 2022 09:41:00 +0000</pubDate>
      <link>https://dev.to/snakepy/use-your-own-docker-images-with-a-github-workflow-5gj6</link>
      <guid>https://dev.to/snakepy/use-your-own-docker-images-with-a-github-workflow-5gj6</guid>
      <description>&lt;p&gt;&lt;strong&gt;In this post, I will show how easily you can set up your own docker images and then utilize GitHub Actions to deploy them to a Docker registry. This will save you set up time and make your environments more stable.&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Git installed and GitHub Account &lt;/li&gt;
&lt;li&gt;Docker installed (for testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get Started with Docker
&lt;/h2&gt;

&lt;p&gt;Docker is a virtualization tool which allows you to require an &lt;strong&gt;image&lt;/strong&gt; which comes with pre-installed configurations and software. So you can pick an operating system of your liking (NO IOS that's illegal) and then get started with installing what you need. These definitions will be written into a Dockerfile. If you don't have docker installed, head to their &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;docs&lt;/a&gt; it is really simple to install.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write your first image
&lt;/h3&gt;

&lt;p&gt;Let's start with a very simple Python image. We will pull the latest distribution of Python and set the &lt;code&gt;PYTHONUNBUFFERED=1&lt;/code&gt; to ensure that all python output is sent to the terminal for better debugging. Paste the below lines into a &lt;code&gt;Dockerfile&lt;/code&gt; (Yes the file has no file extension).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:latest&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have the file, you can now run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--file&lt;/span&gt; Dockerfile &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;Image_Name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--file&lt;/code&gt; can be used in case your Dockerfile has not the default name or is in another path. The &lt;code&gt;-t&lt;/code&gt; flag is used to give the image a name. After the image build successfully on your machine, you can go ahead and try to publish it to &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;hub.docker.com&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Next, we want to tag our image, therefore please create an account at docker hub. We will need the namespace. Before proceeding, please log in locally with docker login.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will log you in and store an encrypted version of your credentials for later use locally.  &lt;/p&gt;

&lt;p&gt;To version our image we use tags, so if one image is bad due to any reason, you can always pull an earlier image. The following command will tag the image. The &lt;code&gt;DOCKER_HUB_NAMESPACE&lt;/code&gt; is by default your username in docker hub. So for me, it would be &lt;code&gt;snakepy&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_HUB_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Concrete example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag python-dev snakepy/python-dev:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, all what is left is to upload the image to docker hub, so you can use it later, therefore you need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_HUB_NAMESPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you have uploaded your image, you can pull it from another Dockerfile and require it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ${DOCKER_HUB_NAMESPACE}/${IMAGE_NAME}:${VERSION}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  WorkFlow for automation
&lt;/h2&gt;

&lt;p&gt;I have created a repository where I upload my images to. The workflow is set up in a way that if I push to that repository it will automatically build all images and push them to docker hub with a new version. I also created scheduled tasks, which runs periodically to release the latest version of the image. &lt;/p&gt;

&lt;p&gt;This is where the fun begins and once you have your automation set up you can refine it, and it will grow overtime. You can have a look at my &lt;a href="https://github.com/snake-py/docker-images" rel="noopener noreferrer"&gt;current&lt;/a&gt; set up.&lt;/p&gt;

&lt;p&gt;To automate the creation of docker images, you need to do the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a repository for your images&lt;/li&gt;
&lt;li&gt;set the secrets in the repository &lt;/li&gt;
&lt;li&gt;create workflow file&lt;/li&gt;
&lt;li&gt;profit 💰 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First create the GitHub repository and then find the secrets tab, there you can add new secrets. You will need to add &lt;code&gt;DOCKER_HUB_NAMESPACE&lt;/code&gt;, &lt;code&gt;DOCKER_HUB_PASSWORD&lt;/code&gt; and&lt;code&gt;DOCKER_HUB_USER&lt;/code&gt;. You can either set them as repository or workflow env. I found it easier to simply set them as repository variable.  &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%2Fjw1erjt67mlm6bwuaibs.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%2Fjw1erjt67mlm6bwuaibs.png" alt="Screenshot of github secrets" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you have added the secrets, we can proceed to the GitHub workflow file. The file(s) need to be inside &lt;code&gt;.github/workflows&lt;/code&gt;. In there I created two files, one for scheduled tasks and one if I push. This will help us to deal easier with the versioning of the images.&lt;br&gt;
&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish-to-docker-hub&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  DOCKER_HUB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_HUB_PASSWORD }}&lt;/span&gt;
&lt;span class="na"&gt;  DOCKER_HUB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_HUB_USER }}&lt;/span&gt;
&lt;span class="na"&gt;  DOCKER_HUB_NAMESPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_HUB_NAMESPACE }}&lt;/span&gt;
&lt;span class="na"&gt;  VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;publish_python&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;python-dev'&lt;/span&gt;
      &lt;span class="na"&gt;LANGUAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;python'&lt;/span&gt;
      &lt;span class="na"&gt;LANGUAGE_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.10&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo ${DOCKER_HUB_PASSWORD} | docker login --username "${DOCKER_HUB_USER}" --password-stdin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build . --file ${IMAGE_NAME}/Dockerfile -t ${IMAGE_NAME}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker tag "${IMAGE_NAME}" "${DOCKER_HUB_NAMESPACE}/${IMAGE_NAME}:${LANGUAGE}${LANGUAGE_VERSION}-${VERSION}"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker push "${DOCKER_HUB_NAMESPACE}/${IMAGE_NAME}:${LANGUAGE}${LANGUAGE_VERSION}-${VERSION}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we define the name of the workflow &lt;code&gt;publish-to-docker-hub&lt;/code&gt; and then the action trigger. Next, we define the variables which will be pulled from the GitHub secret store. Then we define our jobs. Under Jobs, you can have multiple jobs (&lt;a href="https://github.com/snake-py/docker-images/blob/main/.github/workflows/publish-to-docker-hub.yml" rel="noopener noreferrer"&gt;checkout my YAML file&lt;/a&gt;). We are essentially doing what I showed before. We are logging into docker hub, building the image, tagging the image and uploading the image. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please note my repository structure!&lt;/strong&gt; Every Dockerfile is placed like this &lt;code&gt;${IMAGE_NAME}/Dockerfile&lt;/code&gt;. This way I can write small documentations with it or even have config files which I can copy in during build time. &lt;/p&gt;

&lt;p&gt;Now you should see that the action is created for every push you do. I also like an action once a month to release the latest version of the image, just to keep them fresh. &lt;/p&gt;

&lt;p&gt;So I created a second workflow file which is essentially the same and the only difference is the trigger and the VERSION variable.&lt;br&gt;
&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;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    - cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;


&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  DOCKER_HUB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_HUB_PASSWORD }}&lt;/span&gt;
&lt;span class="na"&gt;  DOCKER_HUB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_HUB_USER }}&lt;/span&gt;
&lt;span class="na"&gt;  DOCKER_HUB_NAMESPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_HUB_NAMESPACE }}&lt;/span&gt;
&lt;span class="na"&gt;  VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  LET ME KNOW 🚀
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do you need help, with anything written above?&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What will be your first Image?&lt;/strong&gt; 😄&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you think I can improve - then let me know&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did you like the article?&lt;/strong&gt; 🔥&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check my &lt;a href="https://dev.to/snakepy/my-favorite-laravel-development-environment-with-docker-nginx-php-fpm-xdebug-in-vscode-2o03"&gt;laravel-dev&lt;/a&gt; image out! 🔥&lt;/p&gt;

</description>
      <category>github</category>
      <category>docker</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>5 Things I wished I had known when I started to maintain GitLab CICD</title>
      <dc:creator>Fabio</dc:creator>
      <pubDate>Sat, 16 Jul 2022 11:20:00 +0000</pubDate>
      <link>https://dev.to/snakepy/5-things-i-wished-i-had-known-when-i-started-to-maintain-gitlab-cicd-1e4i</link>
      <guid>https://dev.to/snakepy/5-things-i-wished-i-had-known-when-i-started-to-maintain-gitlab-cicd-1e4i</guid>
      <description>&lt;p&gt;&lt;strong&gt;In this post, I describe a few of the amazing concepts of GitLab CICD pipelines. They made my daily work on the CICD pipelines significantly easier.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All examples can be found &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/tree/main/5%20Things%20I%20wished%20I%20had%20known%20when%20I%20started%20to%20maintain%20GitLab%20CICD" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. YAML Anchors
&lt;/h2&gt;

&lt;p&gt;This is probably for many of you a no-brainer. When I got into CICD I was also pretty new to work with YAML files. I also did not start from scratch. I had to deal with a huge CICD file which was over 1000 lines long. I could shrink it down to a couple of hundreds line while also adding more functionality into the pipelines. I could achive this utilizing the concept of parallelization and YAML Anchors. &lt;/p&gt;

&lt;h3&gt;
  
  
  What are YAML Anchors?
&lt;/h3&gt;

&lt;p&gt;YAML Anchors are reusable code blocks you can easily insert at a later stage. You can define entire jobs like this and based on some variables you set you can change the job. I will make an example.&lt;/p&gt;

&lt;p&gt;Let's say we have two builds in our pipeline to perform, one for development and one for production. But for production, we need a different &lt;code&gt;.env&lt;/code&gt; file than for development. We could just create two jobs like this below, which will result in this kind of &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/pipelines/589435805" rel="noopener noreferrer"&gt;pipeline&lt;/a&gt;:&lt;br&gt;
&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="s"&gt;  - deploy&lt;/span&gt;

&lt;span class="na"&gt;dev_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    - ENV_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env.test&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;deploy&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - source $ENV_FILE&lt;/span&gt;
&lt;span class="s"&gt;    - echo "DEPLOY APPLICATION"&lt;/span&gt;

&lt;span class="na"&gt;master_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    - ENV_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env.prod&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;deploy&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - source $ENV_FILE&lt;/span&gt;
&lt;span class="s"&gt;    - echo "DEPLOY APPLICATION"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two jobs are fairly easy to read, but imagine a more complex build/bundle script and also added different rules on when to run what jobs. We can do better if we use &lt;strong&gt;YAML Anchors&lt;/strong&gt; because most parts of the jobs are the same. So we can transform the above code block to the following, which will result in this kind of &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/pipelines/589436599" rel="noopener noreferrer"&gt;pipeline&lt;/a&gt;:&lt;br&gt;
&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="s"&gt;  - deploy&lt;/span&gt;

&lt;span class="na"&gt;.deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;deploy&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;deploy&lt;/span&gt;
&lt;span class="na"&gt;  before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - if [[ "$ENV_FILE" == '' ]] ; then echo "ENV_FILE is not set" ; exit 1 ; fi&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - source $ENV_FILE&lt;/span&gt;
&lt;span class="s"&gt;    - echo "BUILDING APPLICATION"&lt;/span&gt;

&lt;span class="na"&gt;dev_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  &amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*deploy&lt;/span&gt;
&lt;span class="na"&gt;  variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    ENV_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env.test&lt;/span&gt;

&lt;span class="na"&gt;staging_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  &amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*deploy&lt;/span&gt;

&lt;span class="na"&gt;master_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  &amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*deploy&lt;/span&gt;
&lt;span class="na"&gt;  variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    ENV_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env.prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we now share the code of across the different jobs. &lt;strong&gt;I also added a staging job into the mix to show that we can also prevent jobs from running if the required variables are not set for the job.&lt;/strong&gt; When it comes to override, the stuff which comes at a later line will override the declarations of before. That is why we "spread" the &lt;code&gt;deploy&lt;/code&gt; anchor at the top of the job. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Parallelization
&lt;/h2&gt;

&lt;p&gt;Parallelization has similar use cases to YAML Anchors. But is somewhat different. Sometimes a job is exactly the same, but just one variable is different, and therefore it needs to be run again and again. So going back to the first example, we could also improve on it, in the following manner, which results in this kind of &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/pipelines/589439050" rel="noopener noreferrer"&gt;pipeline&lt;/a&gt;:&lt;br&gt;
&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="s"&gt;  - deploy&lt;/span&gt;

&lt;span class="na"&gt;dev_deploy&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;deploy&lt;/span&gt;
&lt;span class="na"&gt;  parallel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;      - ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;          - test&lt;/span&gt;
&lt;span class="s"&gt;          - prod&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo $ENV&lt;/span&gt;
&lt;span class="s"&gt;    - echo .env.$ENV&lt;/span&gt;
&lt;span class="s"&gt;    - source .env.$ENV&lt;/span&gt;
&lt;span class="s"&gt;    - echo "DEPLOY TO $ENV"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So instead of define multiple jobs, we define one job with a parallel matrix. This will spin up the job multiple times and inject the &lt;code&gt;ENV&lt;/code&gt; variable. This is very useful if you for instance need to build or test your app based on different environments files, because then only one CI Variable is different. The downside is that you can only spin up 50 parallel processes.&lt;/p&gt;

&lt;p&gt;On the other hand, parallel jobs are often used to split up a big job into smaller parts and then bring everything together in the next job, or you can split your test files into parallel jobs.  &lt;/p&gt;

&lt;h2&gt;
  
  
  3.  &lt;code&gt;CI_JOB_TOKEN&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;CI_JOB_TOKEN&lt;/code&gt; is a pre-set &lt;a href="https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html" rel="noopener noreferrer"&gt;variable&lt;/a&gt; which allows you to access or trigger other resources within a group. So if you need to trigger a multi project pipeline where for instance after the backend is deployed you want to trigger the frontend deployment the &lt;code&gt;CI_JOB_TOKEN&lt;/code&gt; comes in very handy. But there is more! If you use the &lt;code&gt;CI_JOB_TOKEN&lt;/code&gt; then GitLab will actually know and make a connection between these pipelines. You can jump from one project's pipeline to another project's pipeline. The call would look like this:&lt;br&gt;
&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="s"&gt;  - trigger_pipeline_in_other_project&lt;/span&gt;

&lt;span class="na"&gt;trigger&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;trigger_pipeline_in_other_project&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - curl --request POST --form token=${CI_JOB_TOKEN} https://gitlab.com/api/v4/projects/&amp;lt;PROJECT_ID&amp;gt;/trigger/pipeline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A resulting pipeline could look like 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%2Fnrg40sqp3y00icg2djyq.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%2Fnrg40sqp3y00icg2djyq.PNG" alt="Example of the use of the CI_JOB_TOKEN"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Clean Up Jobs
&lt;/h2&gt;

&lt;p&gt;Clean up jobs are jobs which run after another job and based on the pipeline status the execution changes. So you can basically run a different job depending on the pipeline status. For instance, you can then clear the cache on failure or invalidate some CloudFront dist etc. So to utilize this concept you can do something like the following, which result in a pipeline like &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/pipelines/589451267" rel="noopener noreferrer"&gt;this&lt;/a&gt;:&lt;br&gt;
&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="s"&gt;  - build&lt;/span&gt;
&lt;span class="s"&gt;  - deploy&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;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "BUILD APPLICATION"&lt;/span&gt;

&lt;span class="na"&gt;deploy_on_failure&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;deploy&lt;/span&gt;
&lt;span class="na"&gt;  when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on_failure&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "CLEAR ARTIFACTS"&lt;/span&gt;

&lt;span class="na"&gt;deploy_on_success&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;deploy&lt;/span&gt;
&lt;span class="na"&gt;  when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on_success&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "DEPLOY APPLICATION"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;deploy_on_failure&lt;/code&gt; runs only if the build has failed, while &lt;code&gt;deploy_on_success&lt;/code&gt; will run when the build has succeeded. This can come very handy but has limitations, that is why I really like the next concept. &lt;/p&gt;

&lt;h2&gt;
  
  
  5. Child Pipelines &amp;amp; Dynamic Child Pipelines
&lt;/h2&gt;

&lt;p&gt;Child Pipelines are pipelines which are started using the combination of the &lt;code&gt;trigger&lt;/code&gt; and &lt;code&gt;include&lt;/code&gt; keywords. They are detached from the parent pipeline and start by default directly running when triggered. So if one stage in your CICD file is "trigger job" for a child pipeline, it will trigger the pipeline and then the next job will start immediately after. Child pipelines are defined in a second CICD file, which is included into the main file. Let me make an example, which would result in this kind of &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/pipelines/589447590" rel="noopener noreferrer"&gt;pipeline&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;The main file would look like this:&lt;br&gt;
&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="s"&gt;  - test&lt;/span&gt;
&lt;span class="s"&gt;  - build&lt;/span&gt;
&lt;span class="s"&gt;  - deploy&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;  trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test.yml&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;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "BUILD APPLICATION"&lt;/span&gt;

&lt;span class="na"&gt;deploy&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;deploy&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "DEPLOY APPLICATION"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the test stage includes a second YAML file, which will be triggered into the detached (child) pipeline. The file could look like this:&lt;br&gt;
&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="s"&gt;  - test&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;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "TEST APPLICATION"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, child pipelines allow splitting your YAML files into multiple files. But they also have a constraint and can only be triggered up to two levels down. That means the first child pipeline can trigger another child pipeline, but this pipeline cannot trigger a third child pipeline. But why is this exciting, we can use other tools for splitting YAML files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is exciting because the triggered YAML File does not have to exist before the pipeline starts!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The above statement leads us right into &lt;strong&gt;Dynamic Child Pipelines&lt;/strong&gt;. This concept is really powerful and deserves an article on its own (Let me know if I should write more about it). &lt;/p&gt;

&lt;p&gt;Most programming languages have some sort of packages to convert a JSON like structure into a YAML file. So what you can do, you have a pre-job which will compute the YAML file for you and then passes the YAML file as an artifact to the trigger job. This way, you can decide on the fly what the child pipeline should look like. &lt;/p&gt;

&lt;p&gt;What I am going to show is not the most elegant or dynamic way, but it is the easiest way to grasp the concept. I call this set up a &lt;strong&gt;pipeline switch&lt;/strong&gt;.  Let's say we have a job which computes something for us. &lt;/p&gt;

&lt;p&gt;Example conditions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For instance gas prices for blockchains and if the gas prices are low we want to deploy the new contracts (basically when deployment costs are low)&lt;/li&gt;
&lt;li&gt;On every Sunday we want to deploy our frontend in a random color 😆&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get the gist, so we have some condition on which we want to alter the pipeline.&lt;/p&gt;

&lt;p&gt;In the below example the pipeline depends on the outcome of the condition (the deployment fees):&lt;br&gt;
&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="s"&gt;  - build&lt;/span&gt;
&lt;span class="s"&gt;  - check_deployment_costs&lt;/span&gt;
&lt;span class="s"&gt;  - trigger_dynamic_child&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;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "BUILD APPLICATION"&lt;/span&gt;

&lt;span class="na"&gt;check_deployment_costs&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;check_deployment_costs&lt;/span&gt;
&lt;span class="na"&gt;  script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;    - echo "RUNS SCRIPT TO CHECK DEPLOYMENT COSTS"&lt;/span&gt;
&lt;span class="s"&gt;    - echo "query computed costs per contract are 50 Finney"&lt;/span&gt;
&lt;span class="s"&gt;    - bash pipelineSwitch.sh &lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;
&lt;span class="na"&gt;  artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - './dynamicChildPipeline.yml'&lt;/span&gt;

&lt;span class="na"&gt;trigger_dynamic_child&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;trigger_dynamic_child&lt;/span&gt;
&lt;span class="na"&gt;  trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;      - artifact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dynamicChildPipeline.yml&lt;/span&gt;
&lt;span class="na"&gt;        job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check_deployment_costs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in the step &lt;code&gt;check_deployment_costs&lt;/code&gt; we check for the deployment costs and plug that into our bash script. The bash script is a simple check and then copies from the template folder to the location from where we will upload the artifact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"input value: &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &amp;lt; 51 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"should deployment"&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; ./CICDTemplates/deployment.yml ./dynamicChildPipeline.yml
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"should wait deployment"&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; ./CICDTemplates/sendNotification.yml ./dynamicChildPipeline.yml
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This solution might be as stated earlier not as elegant as other solutions but still pretty viable for a quick way. The resulting pipelines look like this, if &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/pipelines/589455462" rel="noopener noreferrer"&gt;the price is too high&lt;/a&gt; or &lt;a href="https://gitlab.com/f.batti/dev-to-examples/-/pipelines/589455829" rel="noopener noreferrer"&gt;if the price is okay&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  LET ME KNOW 🚀
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do you need help, with anything written above?&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What would be top on your list?&lt;/strong&gt; 😄&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you think I can improve - then let me know&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did you like the article?&lt;/strong&gt; 🔥&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>gitlab</category>
      <category>ci</category>
      <category>cd</category>
    </item>
    <item>
      <title>My favorite Laravel development environment, with Docker, Nginx, PHP-FPM Xdebug in VSCode</title>
      <dc:creator>Fabio</dc:creator>
      <pubDate>Fri, 08 Jul 2022 19:20:00 +0000</pubDate>
      <link>https://dev.to/snakepy/my-favorite-laravel-development-environment-with-docker-nginx-php-fpm-xdebug-in-vscode-2o03</link>
      <guid>https://dev.to/snakepy/my-favorite-laravel-development-environment-with-docker-nginx-php-fpm-xdebug-in-vscode-2o03</guid>
      <description>&lt;p&gt;&lt;strong&gt;In my &lt;a href="https://dev.to/snakepy/how-to-debug-laravel-apps-with-laravel-apps-with-xdebuger-in-vs-code-8cp"&gt;last post&lt;/a&gt; I showed how you can set up XDebug for a simple dev environment, which used &lt;code&gt;php artisan serve&lt;/code&gt;. However, what I am showing today is actually more perfomant. Once you understand the set up, it will probably soon be your favorite Laravel dev environment as well.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The setup can be found in this &lt;a href="https://github.com/snake-py/laravel-docker-nginx" rel="noopener noreferrer"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Content &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why should I use the set up 😎&lt;/li&gt;
&lt;li&gt;Requirements&lt;/li&gt;
&lt;li&gt;
Set Up Guide

&lt;ul&gt;
&lt;li&gt;Break Down&lt;/li&gt;
&lt;li&gt;Dockerfiles and Xdebug Configs&lt;/li&gt;
&lt;li&gt;ENV Vars and docker-compose&lt;/li&gt;
&lt;li&gt;NGINX Configs&lt;/li&gt;
&lt;li&gt;Set Permissions&lt;/li&gt;
&lt;li&gt;Redis Lib (optional)&lt;/li&gt;
&lt;li&gt;First Run&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;VS Code Set Up&lt;/li&gt;

&lt;li&gt;

Troubleshooting 💥 &lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why should I use the set up &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The set up is great, because you will be able to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Nginx and php-fpm to process requests much faster&lt;/li&gt;
&lt;li&gt;Use the debugger with Nginx calls&lt;/li&gt;
&lt;li&gt;Use the debugger for the queue &lt;/li&gt;
&lt;li&gt;Have a redis cache set up&lt;/li&gt;
&lt;li&gt;Have a database set up&lt;/li&gt;
&lt;li&gt;Have a &lt;code&gt;php artisan serve&lt;/code&gt; server as fallback
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nginx with php-fpm enables parallel processing of requests, while with just the &lt;code&gt;php artisan serve&lt;/code&gt; you will only be able to process requests in sequence. &lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;only requirement&lt;/strong&gt; is that you have a newer version of &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;docker installed&lt;/a&gt;. (In older docker versions you might have to manually install docker-compose as well).&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up Guide &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Break Down &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create the Dockerfiles - I am gonna use an image that I have created for this guide (&lt;a href="https://github.com/snake-py/laravel-dev-image" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or from &lt;a href="https://hub.docker.com/r/snakepy/laravel-dev-image" rel="noopener noreferrer"&gt;hub.docker&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Create the XDebug configs&lt;/li&gt;
&lt;li&gt;Set the .env vars&lt;/li&gt;
&lt;li&gt;Create the docker-compose&lt;/li&gt;
&lt;li&gt;Create the Nginx config&lt;/li&gt;
&lt;li&gt;Set the correct permissions for &lt;code&gt;storage&lt;/code&gt; and &lt;code&gt;bootstrap&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(optional) Add a library for Redis&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Dockerfiles and Xdebug Configs &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;We are going to need 3 files, 2 &lt;code&gt;Dockerfiles&lt;/code&gt; and one &lt;code&gt;docker-compose.yml&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;First the main &lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/Dockerfile" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt; for the actual app container:&lt;/p&gt;

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

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; snakepy/laravel-dev-image:php7.4-a696761a6b37e2480ba83edc4edee9a7632f3332&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; .docker/xdebug.ini /etc/php/7.4/cli/conf.d/99-xdebug.ini&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You can see we are pulling the image which comes with all the plugins you will need. (I plan to release a PHP 8 image as well, let me know if you guys also are interested in a production image ✌️). Next we copy configs for debugging on the &lt;code&gt;php artisan serve&lt;/code&gt; container. They can be found at (&lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/.docker/xdebug.ini" rel="noopener noreferrer"&gt;.docker/xdebug.ini&lt;/a&gt;): &lt;/p&gt;

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

&lt;span class="n"&gt;zend_extension&lt;/span&gt; = &lt;span class="n"&gt;xdebug&lt;/span&gt;

&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_enable&lt;/span&gt;=&lt;span class="n"&gt;on&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_autostart&lt;/span&gt; = &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;develop&lt;/span&gt;,&lt;span class="n"&gt;gcstats&lt;/span&gt;,&lt;span class="n"&gt;coverage&lt;/span&gt;,&lt;span class="n"&gt;profile&lt;/span&gt;,&lt;span class="n"&gt;debug&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;idekey&lt;/span&gt;=&lt;span class="n"&gt;docker&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;start_with_request&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
;&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;=/&lt;span class="n"&gt;tmp&lt;/span&gt;/&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;client_port&lt;/span&gt;=&lt;span class="m"&gt;9003&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;client_host&lt;/span&gt;=&lt;span class="s1"&gt;'host.docker.internal'&lt;/span&gt; 
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;discover_client_host&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now we need to define a second docker file for serving the app with PHP-FPM. We need to do that because Nginx does not come with a PHP plugin. If we'd use Apache we could directly serve from Apache. &lt;/p&gt;

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

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; php:7.4-fpm&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pecl &lt;span class="nb"&gt;install &lt;/span&gt;xdebug-2.9.2 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker-php-ext-enable xdebug  

&lt;span class="k"&gt;RUN &lt;/span&gt;docker-php-ext-install mysqli pdo pdo_mysql

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; .docker/xdebug-nginx.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; here we are not pulling the image I have prepared, it would be overkill, hence we are going with the default image from PHP. The Xdebug configs are being copied as well, but they are slightly different. See at (&lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/.docker/xdebug-nginx.ini" rel="noopener noreferrer"&gt;.docker/xdebug-nginx.ini&lt;/a&gt;):&lt;/p&gt;

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

&lt;span class="n"&gt;zend_extension&lt;/span&gt; = &lt;span class="n"&gt;xdebug&lt;/span&gt;

&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_enable&lt;/span&gt;=&lt;span class="n"&gt;on&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_autostart&lt;/span&gt; = &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;default_enable&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;mode&lt;/span&gt;=&lt;span class="n"&gt;develop&lt;/span&gt;,&lt;span class="n"&gt;gcstats&lt;/span&gt;,&lt;span class="n"&gt;coverage&lt;/span&gt;,&lt;span class="n"&gt;profile&lt;/span&gt;,&lt;span class="n"&gt;debug&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;idekey&lt;/span&gt;=&lt;span class="n"&gt;docker&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;start_with_request&lt;/span&gt;=&lt;span class="n"&gt;yes&lt;/span&gt;
;&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;=/&lt;span class="n"&gt;tmp&lt;/span&gt;/&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;
;&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_log&lt;/span&gt;=/&lt;span class="n"&gt;tmp&lt;/span&gt;/&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;client_host&lt;/span&gt;=&lt;span class="s1"&gt;'host.docker.internal'&lt;/span&gt; 
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;discover_client_host&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_handler&lt;/span&gt;=&lt;span class="n"&gt;dbgp&lt;/span&gt;


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

&lt;/div&gt;
&lt;h5&gt;
  
  
  ENV Vars and docker-compose &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;This is by far the hardest part to get right. The docker-compose file I am going to present requires some .env vars, hence I am going to show these first.&lt;/p&gt;

&lt;p&gt;Please set in &lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/.env.example" rel="noopener noreferrer"&gt;.env&lt;/a&gt; following vars: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Docker Ports
DOCKER_EXPOSED_DB_PORT=3307
DOCKER_EXPOSED_NGINX_PORT=10000
DOCKER_EXPOSED_ARTISAN_SERVE_PORT=8000

## can be retrieved with hostename -I
DOCKER_NGINX_LOCAL_IP=

## DO NOT CHANGE!If you change this, you also need to change DB_HOST and REDIS_HOST
DOCKER_APP_CONTAINER_NAME="laravel-dev-to"

DB_CONNECTION=mysql
DB_HOST=laravel-dev-to-db
DB_PORT=3306
DB_DATABASE=test
DB_USERNAME=test
DB_PASSWORD=Password123!

REDIS_HOST=laravel-dev-to-cache
REDIS_PASSWORD=REDIS_PASSWORD
REDIS_PORT=6379


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

&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;DOCKER_APP_CONTAINER_NAME&lt;/code&gt; is used as a prefix in the docker-compose hence the naming convention to &lt;code&gt;DB_HOST&lt;/code&gt; and &lt;code&gt;REDIS_HOST&lt;/code&gt; must be maintained. &lt;/p&gt;

&lt;p&gt;Let's look at the &lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/docker-compose.yml" rel="noopener noreferrer"&gt;docker-compose.yml&lt;/a&gt;: &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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOCKER_APP_CONTAINER_NAME}&lt;/span&gt;
        &lt;span class="na"&gt;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&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;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
            &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php artisan serve --host=0.0.0.0&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;
        &lt;span class="na"&gt;ports&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_EXPOSED_ARTISAN_SERVE_PORT}:8000&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;composer&lt;/span&gt;
        &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxynet&lt;/span&gt;
        &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;queue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOCKER_APP_CONTAINER_NAME}-queue&lt;/span&gt;
        &lt;span class="na"&gt;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&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;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
            &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;php&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;artisan&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;queue:work'&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REDIS_HOST=${REDIS_HOST}&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REDIS_PASSWORD=${REDIS_PASSWORD}&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REDIS_PORT=${REDIS_PORT}&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cache&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;   
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;composer&lt;/span&gt;
        &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxynet&lt;/span&gt;     
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOCKER_APP_CONTAINER_NAME}-db&lt;/span&gt;
        &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/x86_64&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
        &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no"&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
            &lt;span class="na"&gt;MYSQL_ROOT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;root&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_DATABASE}&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;${DB_USERNAME}&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;${DB_PASSWORD}&lt;/span&gt;
        &lt;span class="na"&gt;ports&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_EXPOSED_DB_PORT}:3306&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_data:/var/lib/mysql&lt;/span&gt;
        &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxynet&lt;/span&gt;
        &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&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;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOCKER_APP_CONTAINER_NAME}-cache&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server --requirepass ${REDIS_PASSWORD}&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:5.0&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;:6379&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cache_data:/data&lt;/span&gt;
        &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxynet&lt;/span&gt;
    &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:stable-alpine&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOCKER_APP_CONTAINER_NAME}-nginx&lt;/span&gt;
        &lt;span class="na"&gt;ports&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_EXPOSED_NGINX_PORT}:80&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./:/var/www/html&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./.docker/nginx/default.conf:/etc/nginx/conf.d/default.temp&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;composer&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cache&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/bin/sh -c "envsubst '$$DOCKER_NGINX_LOCAL_IP $$DOCKER_APP_CONTAINER_NAME' &amp;lt; /etc/nginx/conf.d/default.temp &amp;gt; /etc/nginx/conf.d/default.conf &amp;amp;&amp;amp; exec nginx -g 'daemon off;'"&lt;/span&gt;
        &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
        &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxynet&lt;/span&gt;
    &lt;span class="na"&gt;php-fpm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&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;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.PHP-FPM&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOCKER_APP_CONTAINER_NAME}-php-fpm&lt;/span&gt;
        &lt;span class="na"&gt;volumes&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/xdebug-nginx.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./:/var/www/html&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:9000"&lt;/span&gt;
        &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;composer&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cache&lt;/span&gt;
        &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxynet&lt;/span&gt;
    &lt;span class="na"&gt;composer&lt;/span&gt;&lt;span class="pi"&gt;:&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;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
            &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DOCKER_APP_CONTAINER_NAME}-composer&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer install&lt;/span&gt;
        &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;proxynet&lt;/span&gt;
        &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local"&lt;/span&gt;
  &lt;span class="na"&gt;cache_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local"&lt;/span&gt;


&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;proxynet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;portal&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Okay I am going to break down the docker-compose file so actually understand what going on there. It will build the following containers: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app the basic Laravel dev app&lt;/li&gt;
&lt;li&gt;db a MySQL database&lt;/li&gt;
&lt;li&gt;cache Redis cache&lt;/li&gt;
&lt;li&gt;queue basic Laravel dev queue worker&lt;/li&gt;
&lt;li&gt;Nginx the web server for serving to php-fpm and serving static content&lt;/li&gt;
&lt;li&gt;php-fpm server for executing PHP code for Nginx&lt;/li&gt;
&lt;li&gt;composer runs an install on container boot and will the exit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these containers are being wrapped into a proxynet in case you want to use the same network in another compose file like a Vue frontend. &lt;/p&gt;

&lt;p&gt;The variables from the .env file are being used to determine which ports are going to be exposed. In my example we are exposing &lt;code&gt;php artisan serve&lt;/code&gt; on port 8000 and &lt;code&gt;nginx&lt;/code&gt; on port 10000. The &lt;code&gt;mysql db&lt;/code&gt; will be exposed on port 3307.&lt;/p&gt;

&lt;p&gt;The last thing I want to point out about the compose file is the use of:&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;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Which basically tells docker to resolve docker-container-names to their network IP address. &lt;/p&gt;

&lt;p&gt;In the next section we are going to take a closer look at the Nginx compose section and configs.  &lt;/p&gt;

&lt;h4&gt;
  
  
  NGINX Configs &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Let us talk about the Nginx volumes. Nginx mounts two volumes. &lt;code&gt;./:/var/www/html&lt;/code&gt; will be used for static file serving, like images. &lt;code&gt;./.docker/nginx/default.conf:/etc/nginx/conf.d/default.temp&lt;/code&gt; this volume are the actual nginx configs. If you know Nginx then you will notice that we actually put a "wrong" file name into the image &lt;code&gt;default.temp&lt;/code&gt;. We do not mount the Nginx to the end/correct location because we require &lt;code&gt;envsubst&lt;/code&gt; to run over it and replace two variables in the nginx.conf file.&lt;code&gt;ènvsubst&lt;/code&gt; has the ability to run over a config file and replace only the variables with the names which were provided to it. I think this &lt;a href="https://unix.stackexchange.com/a/294400/524330" rel="noopener noreferrer"&gt;answer&lt;/a&gt; describes the issue very well. In the &lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/docker-compose.yml#L85" rel="noopener noreferrer"&gt;command&lt;/a&gt; &lt;code&gt;envsubst&lt;/code&gt; we define the correct output path for nginx and run the Nginx deamon.    &lt;/p&gt;

&lt;p&gt;These Varibles need to be replaced and the other should not be touched: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;$DOCKER_APP_CONTAINER_NAME needs to replaced so nginx can proxy all php requests to php-fpm&lt;/li&gt;
&lt;li&gt;$DOCKER_NGINX_LOCAL_IP is required for XDebug&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;❗ I had a lot of difficulty getting it right, but note that you need to escape the variables for docker with two dollar signs &lt;code&gt;$$&lt;/code&gt;. ❗&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

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

envsubst &lt;span class="s1"&gt;'$$DOCKER_NGINX_LOCAL_IP $$DOCKER_APP_CONTAINER_NAME'&lt;/span&gt; 


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

&lt;/div&gt;

&lt;p&gt;Here is the Nginx config template you need to copy into your project, I have them at (&lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/.docker/nginx/default.conf" rel="noopener noreferrer"&gt;.docker/nginx/default.conf&lt;/a&gt;):&lt;/p&gt;

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

&lt;span class="n"&gt;server&lt;/span&gt; {
    &lt;span class="n"&gt;listen&lt;/span&gt;  &lt;span class="m"&gt;80&lt;/span&gt;;

    &lt;span class="c"&gt;# this path MUST be exactly as docker-compose.fpm.volumes,
&lt;/span&gt;    &lt;span class="c"&gt;# even if it doesn't exist in this dock.
&lt;/span&gt;    &lt;span class="n"&gt;root&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;www&lt;/span&gt;/&lt;span class="n"&gt;html&lt;/span&gt;/&lt;span class="n"&gt;public&lt;/span&gt;;

    &lt;span class="n"&gt;location&lt;/span&gt; / {
        &lt;span class="n"&gt;try_files&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt; /&lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;$&lt;span class="n"&gt;is_args&lt;/span&gt;$&lt;span class="n"&gt;args&lt;/span&gt;;
    }

    &lt;span class="n"&gt;location&lt;/span&gt; ~ ^/.+\.&lt;span class="n"&gt;php&lt;/span&gt;(/|$) {
        &lt;span class="n"&gt;fastcgi_pass&lt;/span&gt; $&lt;span class="n"&gt;DOCKER_APP_CONTAINER_NAME&lt;/span&gt;-&lt;span class="n"&gt;php&lt;/span&gt;-&lt;span class="n"&gt;fpm&lt;/span&gt;:&lt;span class="m"&gt;9000&lt;/span&gt;;
        &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;fastcgi_params&lt;/span&gt;;
        &lt;span class="n"&gt;fastcgi_param&lt;/span&gt; &lt;span class="n"&gt;SCRIPT_FILENAME&lt;/span&gt; $&lt;span class="n"&gt;document_root&lt;/span&gt;$&lt;span class="n"&gt;fastcgi_script_name&lt;/span&gt;;

        &lt;span class="n"&gt;fastcgi_param&lt;/span&gt; &lt;span class="n"&gt;PATH_INFO&lt;/span&gt; $&lt;span class="n"&gt;fastcgi_path_info&lt;/span&gt;;
        &lt;span class="n"&gt;fastcgi_param&lt;/span&gt; &lt;span class="n"&gt;PHP_VALUE&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_autostart&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_enable&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_host&lt;/span&gt;=$&lt;span class="n"&gt;DOCKER_NGINX_LOCAL_IP&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;;
    }
}


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  Set Permissions &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Simply give the ./storage and ./bootstrap folder these permissions:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 777 ./storage
&lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 777 ./bootstrap


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  Redis Lib (optional) &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;I am going to connect to redis using the libary &lt;a href="https://github.com/predis/predis" rel="noopener noreferrer"&gt;predis&lt;/a&gt;. Simply add the following to your compose.json&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="nl"&gt;"require"&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"predis/predis"&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.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and inside &lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/config/database.php#L122" rel="noopener noreferrer"&gt;config/databse.php:122&lt;/a&gt; I switched to use predis. &lt;/p&gt;

&lt;h4&gt;
  
  
  First Run 🏁 &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;If you have followed the previous steps now is the moment of truth: 💦 💦&lt;/p&gt;

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

docker compose run


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

&lt;/div&gt;

&lt;p&gt;If this is the first boot and you had no vendor folder prior to this - then wait till the compose container exits and shut the container down and rerun &lt;code&gt;docker compose up&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;now you should be able to hit 🚀🚀🚀: &lt;br&gt;
&lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;&lt;br&gt;
and&lt;br&gt;
&lt;a href="http://localhost:10000" rel="noopener noreferrer"&gt;http://localhost:10000&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything is opening up as expected then you can continue with the IDE set up for XDebug. If not then have a look a the &lt;a href="https://github.com/snake-py/laravel-docker-nginx" rel="noopener noreferrer"&gt;github repo&lt;/a&gt; or leave me a comment and I can try to assist you. Also have a look the troubleshooting section. &lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up VS Code &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now the set up for VS Code to actually use the debugger you need first to install the following &lt;a href="https://github.com/xdebug/vscode-php-debug" rel="noopener noreferrer"&gt;extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then copy over the &lt;a href="https://github.com/snake-py/laravel-docker-nginx/blob/main/.vscode/launch.json" rel="noopener noreferrer"&gt;launch.json&lt;/a&gt; file I have prepared. You will find multiple configurations in there. I will go over one so you understand whats going on. &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;"Listen for Xdebug inside docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pathMappings"&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;"/app"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceRoot}/"&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;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9003&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"log"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; shows up in the debugger drop down&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hostname&lt;/code&gt; needs to be set to 0.0.0.0 so the server can find the XDebug report from localhost&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pathMappings&lt;/code&gt; is basically a map where the folders are placed on the server =&amp;gt; &lt;code&gt;{"path_to_app_inside_docker": "${workspaceRoot}/"}&lt;/code&gt; the &lt;code&gt;workspaceRoot&lt;/code&gt; is automatically set by VS Code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;port&lt;/code&gt; the port on which XDebug will answer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to debug now, you will need to run &lt;code&gt;docker compose up&lt;/code&gt; once the containers are running you can choose from the drop down, on which requests you want to listen to. I usually just launch two debugger instances:&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%2Ff89t4pjvv7pxbg34ua9a.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%2Ff89t4pjvv7pxbg34ua9a.png" alt="VSCode Debugger tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Listen for Xdebug inside docker"
and&lt;/li&gt;
&lt;li&gt;"Listen for Xdebug inside docker nginx"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the debugger is launched you should be able to hit break points, if requests are going to &lt;a href="http://loclahost:8000" rel="noopener noreferrer"&gt;http://loclahost:8000&lt;/a&gt; or &lt;a href="http://localhost:10000" rel="noopener noreferrer"&gt;http://localhost:10000&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;To test this insert this into &lt;code&gt;api.php&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&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;'/test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&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="nf"&gt;response&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;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Visit my portfolio site at snake-py.com'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;200&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;The set a a break point on the return statement line and try to hit the route &lt;a href="http://localhost:8000/api/test" rel="noopener noreferrer"&gt;http://localhost:8000/api/test&lt;/a&gt; or &lt;a href="http://localhost:10000/api/test" rel="noopener noreferrer"&gt;http://localhost:10000/api/test&lt;/a&gt;&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%2Fo2vpjxvs739gimcb5nka.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%2Fo2vpjxvs739gimcb5nka.png" alt="Break point is being hittet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;There is actually a lot of stuff which might go wrong during your first set up. I just want to mention here a few ways which helped me debug. (I also try to keep this a little updated). Feel free to open a GitHub issue or comment here for support. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugger does not connect?&lt;/strong&gt;&lt;br&gt;
Enable the following config, which will output a log file wiht an error you can google. &lt;/p&gt;

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

&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;=/&lt;span class="n"&gt;tmp&lt;/span&gt;/&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;
&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;remote_log&lt;/span&gt;=/&lt;span class="n"&gt;tmp&lt;/span&gt;/&lt;span class="n"&gt;xdebug&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The log file will be in the app container or the php-fpm container depending on which port your hitting. To get into the php-fpm container is no bash installed yo you need execute:&lt;/p&gt;

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

docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;php-fpm-container-name&amp;gt; bin/sh


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Database or cache does not connect properly?&lt;/strong&gt;&lt;br&gt;
If you are sure that the containers can reach each other and that the names you have provided in the .env file are correct you can go inside the app container and run a config clear. &lt;/p&gt;

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

docker ps &lt;span class="c"&gt;# to get the name of the app container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;container_name&amp;gt; bash
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan migrate 


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

&lt;/div&gt;

&lt;p&gt;If one of these commands fails then you usually did not provide the correct name or port in the .env file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nginx is failing on boot?&lt;/strong&gt;&lt;br&gt;
Did you set the correct local ip address? Is the php-fpm container name correctly prefixed? Check with &lt;code&gt;docker ps&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It should look like 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%2Fzxigsjx3y9kcap0t1zc6.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%2Fzxigsjx3y9kcap0t1zc6.png" alt="Example of the running containers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope I could help you with this set up let me know what you guys might think! 😄 &lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>laravel</category>
      <category>docker</category>
    </item>
    <item>
      <title>How to debug Laravel with XDebuger in VS Code</title>
      <dc:creator>Fabio</dc:creator>
      <pubDate>Tue, 05 Jul 2022 19:07:05 +0000</pubDate>
      <link>https://dev.to/snakepy/how-to-debug-laravel-apps-with-laravel-apps-with-xdebuger-in-vs-code-8cp</link>
      <guid>https://dev.to/snakepy/how-to-debug-laravel-apps-with-laravel-apps-with-xdebuger-in-vs-code-8cp</guid>
      <description>&lt;p&gt;To be able to debug Laravel applications during development will give you a huge advantage, compared to your colleagues who are struggling their way through using  &lt;code&gt;echo&lt;/code&gt; or &lt;code&gt;print_r&lt;/code&gt; statements. The ability to pause the execution is often crucial to understand how the code works and where the bug is. &lt;/p&gt;

&lt;p&gt;Following steps are required: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install XDebug&lt;/li&gt;
&lt;li&gt;Install xdebug.php-debug for VS Code released by XDebug&lt;/li&gt;
&lt;li&gt;create a launch.json file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first and the second step can sometimes be relatively tricky to get right, however I am confident if you follow my instructions you will get it to work. &lt;/p&gt;

&lt;h2&gt;
  
  
  Install XDebug
&lt;/h2&gt;

&lt;p&gt;(If you have it already installed and are just here, because the other tutorial didn't work, then I have a tip for you. Look at the configs I am providing, which will enable a different the XDebug log level. This often helps to figure out why it is not working.)&lt;/p&gt;

&lt;p&gt;I would recommend that you head over to &lt;a href="https://xdebug.org/docs/install" rel="noopener noreferrer"&gt;https://xdebug.org/docs/install&lt;/a&gt; and look at one of their installation guides, because it will be different for most people. If you use a Unix based system like macOS or Linux you are in luck and the installation will be easier, because you most likely can install it with PECL or Homebrew If this is not possible, simply download the zipped file and follow the instructions on their wizard page. &lt;a href="https://xdebug.org/wizard" rel="noopener noreferrer"&gt;https://xdebug.org/wizard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same goes for Windows user. On Windows you might even first update your PHP Version since not all versions are compatible. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take a way&lt;/strong&gt; - the installation of XDebug will be different for most OS and PHP versions, hence it is almost impossible to give a general how to guide. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the main page and see if you can install it using CLI tools &lt;a href="https://xdebug.org/docs/install" rel="noopener noreferrer"&gt;https://xdebug.org/docs/install&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;If you cannot install it with a CLI tool, head over to the wizard and past the output from &lt;code&gt;php -i&lt;/code&gt; &lt;a href="https://xdebug.org/wizard" rel="noopener noreferrer"&gt;https://xdebug.org/wizard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Check that XDebug is properly installed - you can do that using &lt;code&gt;php -i&lt;/code&gt; and search the output for debug. If it is installed, it should be listed under extensions. (you might need to enable it putting &lt;code&gt;zend_extension = xdebug&lt;/code&gt; into your php.ini file) &lt;/li&gt;
&lt;li&gt;After successful installation adjust the XDebug.config file. (The file will be named differently depending on your OS. The wizard should have told you how it was named.) If you run &lt;code&gt;php -i | grep xdebug&lt;/code&gt; you should be able to see the file you need to configure. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To get the configs right can sometimes be quite difficult. However, the installation is actually the hardest part. Once you know where you can set the settings for XDebug it gets actually simpler. &lt;/p&gt;

&lt;p&gt;Below, you can see possible configs for XDebug. I added comments to each line, so you actually understand what it does. Often you will see that the boolean fields are being set with either &lt;code&gt;on&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;yes&lt;/code&gt; - again different version need different values. However, for me, it was never an issue and could actually use these settings interchangeable.&lt;br&gt;
&lt;/p&gt;

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

xdebug.remote_enable=on ; enables the extension
xdebug.remote_autostart = 1 ; required to be true so xdebug is started for ever php process 
xdebug.start_with_request=yes ; so it turns on for every http request

xdebug.log=/tmp/xdebug.log ; the logging - turn of once evrything works


xdebug.mode=develop,gcstats,coverage,profile,debug ; just give it the modes you need it can take all of them
xdebug.idekey=VSCODE ; can also be phpstorm

xdebug.client_port=9003 ; the port xdebug will be working on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logging actually helped me a lot to set it up correctly, especially in docker container. I will soon release a guide for my favorite dev environment for Laravel. If you want to read more about the different modes, have a look here &lt;a href="https://xdebug.org/docs/upgrade_guide" rel="noopener noreferrer"&gt;https://xdebug.org/docs/upgrade_guide&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xdebug.log=/tmp/xdebug.log 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay now, when you start your Laravel application with &lt;code&gt;php artisan serve&lt;/code&gt; you should see an output where XDebug is complaining that it cannot connect. This will be solved in the next section. &lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up VS Code
&lt;/h2&gt;

&lt;p&gt;First install xdebug.php-debug for VS Code released by XDebug plugin for VS Code. Once done, find the debugger view. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff29l8veog5hw3t4inga9.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff29l8veog5hw3t4inga9.PNG" alt="Shows VS Code debugger view and how to create launch.json" width="388" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the Debugger view you can now create a launch.json file, this will bring down a dropdown in which you simply choose PHP. VsCode will now create a launch.json into the &lt;code&gt;.vscode&lt;/code&gt; folder. In there you will find several configs. We are actually only interested into the first config. Here, it is crucial that the port is the same as the port in the XDebug config file.&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;"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;"Listen for Xdebug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9003&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;Once the port is the same you should be able to hit the play button in the debugger view, make sure you have selected the correct launch configs. Now you should be able to set break points. &lt;/p&gt;

&lt;p&gt;I know I did not give you guys an exact step-by-step guide on how to install XDebug, but this is because it is so different for most people. If many of you struggle with it, I can make a video where I show how I install it. For Windows users, it might even be easier to have a look at my &lt;a href="https://dev.to/snakepy/my-favorite-laravel-development-environment-with-docker-nginx-php-fpm-xdebug-in-vscode-2o03"&gt;article&lt;/a&gt; where I show a docker set up. &lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
