<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Daniel Vigueras</title>
    <description>The latest articles on Forem by Daniel Vigueras (@danielvigueras).</description>
    <link>https://forem.com/danielvigueras</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%2F873635%2Ff7831220-fb02-41d2-a197-54e570233754.jpg</url>
      <title>Forem: Daniel Vigueras</title>
      <link>https://forem.com/danielvigueras</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danielvigueras"/>
    <language>en</language>
    <item>
      <title>Best Practices Setting up Your Local Development Environment</title>
      <dc:creator>Daniel Vigueras</dc:creator>
      <pubDate>Wed, 22 Jun 2022 08:51:12 +0000</pubDate>
      <link>https://forem.com/danielvigueras/best-practices-setting-up-your-local-development-environment-24fj</link>
      <guid>https://forem.com/danielvigueras/best-practices-setting-up-your-local-development-environment-24fj</guid>
      <description>&lt;h1&gt;
  
  
  Best Practices Setting up Your Local Development Environment
&lt;/h1&gt;

&lt;p&gt;You are about to start a new project and then you wonder what's the best option to set up your local development environment. Your OS could be Linux, Mac, or Windows. However other people working on the same project may be running a different OS. There are many alternatives to install and use all the tools you need but, what pros and cons have each one of them?&lt;/p&gt;

&lt;p&gt;This post presents different methods and best practices when setting up your local development environment along with the advantages and drawbacks of each option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Services in your Machine
&lt;/h2&gt;

&lt;p&gt;This is the first alternative when setting up a local environment and is based on manually installing on your computer all the required software. A simple web environment usually includes the Web Server (Apache and Nginx are the most common), the Language Interpreter (PHP/Node/Python/etc.), and a Database Engine (usually MySQL or PostgreSQL).&lt;/p&gt;

&lt;p&gt;So you start downloading, installing, and setting up every component. This can be done by visiting the web page for every component needed and downloading the installer, but the best option is to use a package manager. Package managers will make this process easier and will allow you to keep your software updated to the latest version.&lt;/p&gt;

&lt;p&gt;The specific package manager depends on your Operating System. In macOS the most popular tool is &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt;. In Linux distributions like &lt;em&gt;Ubuntu/Debian&lt;/em&gt;, you can use &lt;a href="https://ubuntu.com/server/docs/package-management" rel="noopener noreferrer"&gt;apt&lt;/a&gt;, and in &lt;em&gt;RedHat/CentOS&lt;/em&gt; distributions you can use &lt;a href="https://www.redhat.com/sysadmin/how-manage-packages" rel="noopener noreferrer"&gt;yum&lt;/a&gt;. In Windows the most popular tool is &lt;a href="https://chocolatey.org/" rel="noopener noreferrer"&gt;Chocolatey&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once all the required software is installed you need to do extra steps to set up the environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the Virtual Host in the Web Server and point it to your language interpreter service&lt;/li&gt;
&lt;li&gt;Create a new User in the Database Engine, a new Database and the give the proper permissions to that User&lt;/li&gt;
&lt;li&gt;Configure your code to use the Database credentials&lt;/li&gt;
&lt;li&gt;Repeat the process for every extra service required in the project (NoSQL databases, caches, etc.)&lt;/li&gt;
&lt;/ul&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%2Fvykje7s3xbv0gu066wu9.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%2Fvykje7s3xbv0gu066wu9.png" alt="Vagrant with Virtual Machine and Services" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This process is not hard once you know how to do it, however, the following problems arise.&lt;/p&gt;

&lt;p&gt;All the devs in the project don't share the same environment, they may have different operating systems and even different software versions. It may trigger situations where tests pass locally but not in CI/CD and vice-versa. Also, a bug that is happening in the computer of a developer may not happen on the computer of another developer.&lt;/p&gt;

&lt;p&gt;Another issue is that if you reinstall your computer you have to repeat every step again. This is boring and tedious, also the installation and configuration method of these packages depends on your operating system. The instructions to set up your local dev environment won't be the same for every member of the project and then they must be written and duplicated for every OS.&lt;/p&gt;

&lt;p&gt;There is a better alternative when setting up a local development environment: using Virtual Machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Virtual Machines
&lt;/h2&gt;

&lt;p&gt;Not every developer in a project will use the same Operating System so the solution is for everyone to run a Virtual Machine (VM) on their computer with the same OS (e.g: Ubuntu Linux 22.04). With this method, all the developers will have the same environment and many problems from the previous section will be solved.&lt;/p&gt;

&lt;p&gt;A free alternative to run Virtual Machines on your computer is &lt;a href="https://www.virtualbox.org/" rel="noopener noreferrer"&gt;VirtualBox&lt;/a&gt;. VirtualBox runs on Mac, Windows, and Linux. Using the interface you can create, start, stop, and destroy Virtual Machines. Once the Virtual Machine is created we can install all the software needed for our development environment. Also, we need to share (mount) the directory where the project files are located on our computer with the Virtual Machine. &lt;/p&gt;

&lt;p&gt;However, creating and setting up a Virtual Machine manually is not the best in terms of productivity. The solution is to use a tool that will allow you to create and set up these environments automatically. This tool is &lt;a href="https://www.vagrantup.com/" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With Vagrant you just need to create a &lt;a href="https://www.vagrantup.com/docs/vagrantfile" rel="noopener noreferrer"&gt;Vagrantfile&lt;/a&gt; in the root directory of your project and then run &lt;code&gt;vagrant up&lt;/code&gt; in the terminal to create and start your Virtual Machine. This is an example of a simple Vagrantfile after running &lt;code&gt;vagrant init ubuntu/jammy64&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Use Ubuntu 22.04 LTS (Jammy Jellyfish) &lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu/jammy64"&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vagrant needs &lt;em&gt;boxes&lt;/em&gt;, which is the package format of installed Operating Systems images. In this example, we are using &lt;code&gt;ubuntu/jammy64&lt;/code&gt; which is an image of Ubuntu's latest LTS version (22.04). Just with this file, we have solved the problem of creating and installing a new Virtual Machine and sharing our project files in the &lt;code&gt;/vagrant&lt;/code&gt; path inside the VM. Boxes can be versioned too, so you can update to the latest version of a box at any moment.&lt;/p&gt;

&lt;p&gt;Now we need to install and set up (provision) the services needed inside the VM. The first option would be to do this manually, but, as in the previous section, there are many issues that we want to avoid. The idea is to do it automatically with a command. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.vagrantup.com/docs/provisioning" rel="noopener noreferrer"&gt;Vagrant supports provisioning&lt;/a&gt; automatically using provisioners. A provisioner could be something like a shell script with every step to install and set up all the software dependencies needed.&lt;/p&gt;

&lt;p&gt;A shell script could work, but the best option is to use an orchestration/automation tool like &lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt;, &lt;a href="https://www.chef.io/" rel="noopener noreferrer"&gt;Chef&lt;/a&gt;, &lt;a href="https://puppet.com/" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt;, or &lt;a href="https://saltproject.io/" rel="noopener noreferrer"&gt;Salt&lt;/a&gt;. Using these tools you can define the steps needed to set up your project in a set of files, and just by invoking &lt;code&gt;vagrant provision&lt;/code&gt; the VM will be set up. The advantage is that setting up a new development environment with this method is easy, fast, and can be shared with all the members of the project. &lt;/p&gt;

&lt;p&gt;Example of a &lt;code&gt;Vagrantfile&lt;/code&gt; using Ubuntu 22.04 and running Ansible with the &lt;code&gt;playbook.yml&lt;/code&gt; file as the initial playbook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Use Ubuntu 22.04 LTS (Jammy Jellyfish) &lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu/jammy64"&lt;/span&gt;

  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Run Ansible from the Vagrant Host&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provision&lt;/span&gt; &lt;span class="s2"&gt;"ansible"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ansible&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;ansible&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;playbook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"playbook.yml"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fabrqyhsmjrxvxybb3ry4.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%2Fabrqyhsmjrxvxybb3ry4.png" alt="Vagrant with Virtual Machine and Services" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This scenario is much better than the previous one where everything is installed directly on your machine. Now there is only one environment and everything is shared within the team and automated. However, there are still some issues that can be improved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Machines are slow to create: The first time you create a new VM it will take some time to download, import, and create the VM in Virtualbox.&lt;/li&gt;
&lt;li&gt;Machines are slow to provision: The first time you provision a VM it will also take some time to download and set up all the packages required by the provisioner.&lt;/li&gt;
&lt;li&gt;High disk space usage: every project requires a new and independent VM, which can take many Gigabytes of storage. It means that if you are working on multiple projects the disk space needed could be very high.&lt;/li&gt;
&lt;li&gt;High memory usage: if you need to have more than one project running at the same time the different VMs needed will consume a lot of memory in your computer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of all these reasons, there is a better alternative than using directly Virtual Machines: using Containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containers
&lt;/h2&gt;

&lt;p&gt;Instead of having to install and set up every service, you can use containers. With containers, you can take profit from existing images built officially. You only need to run a container with the software you need and it will start in a matter of seconds. It will take an extra time the first time because Docker needs to download the image from &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Docker Hub is a container image library where you can find interesting images to use in your projects like the &lt;a href="https://hub.docker.com/_/httpd" rel="noopener noreferrer"&gt;Apache image&lt;/a&gt;, &lt;a href="https://hub.docker.com/_/nginx" rel="noopener noreferrer"&gt;Nginx image&lt;/a&gt;, &lt;a href="https://hub.docker.com/_/php" rel="noopener noreferrer"&gt;PHP image&lt;/a&gt;, &lt;a href="https://hub.docker.com/_/node" rel="noopener noreferrer"&gt;Node.js image&lt;/a&gt;, &lt;a href="https://hub.docker.com/_/mysql" rel="noopener noreferrer"&gt;MySQL image&lt;/a&gt;, etc. These images are ready to use, and they come with instructions about how to use them. Usually, you have to pass environment variables to the container and/or tweak a couple of config files.&lt;/p&gt;

&lt;p&gt;Linux is the only Operating System supporting containers natively, so if you are using macOS or Windows you'll need software to run a VM with Docker in the background for you. Compared with the previous section the advantage is that we will need just one VM for all of our projects, not a VM for every project. The alternatives to running Docker in these systems are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;Docker for Desktop&lt;/a&gt;: The most popular option.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://podman.io/" rel="noopener noreferrer"&gt;Podman&lt;/a&gt;: An alternative container engine with the same CLI interface as the &lt;code&gt;docker&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine a project where you need two containers: a Node.js container and a Database container. One option would be to start both containers from the command line using the &lt;code&gt;docker run&lt;/code&gt; command and passing every argument needed (files to mount, environment variables, ports to publish, etc). However, this is a manual process and is error-prone. There is a better solution to manage a set of containers: &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Docker Compose lets you define a set of containers using a &lt;code&gt;docker-compose.yml&lt;/code&gt; file. This file is in YAML format and defines every container you need along with the configuration parameters.&lt;/p&gt;

&lt;p&gt;Example of &lt;code&gt;docker-compose.yml&lt;/code&gt; file with a PHP 8.1 installation, an Apache Web Server and a MySQL Database:&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;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'&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;image&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:8.1-apache'&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;80: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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.:/var/www/html'&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;mysql&lt;/span&gt;
    &lt;span class="na"&gt;mysql&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mysql:8.0'&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_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rootpassword'&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_ROOT_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%"&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;appdb'&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app'&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;appsecret'&lt;/span&gt;
            &lt;span class="na"&gt;MYSQL_ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having this file in the root directory of your project and running &lt;code&gt;docker compose up&lt;/code&gt; would start both containers (&lt;code&gt;app&lt;/code&gt; and &lt;code&gt;mysql&lt;/code&gt;) in Docker. After working with the project you just need to type &lt;code&gt;docker compose stop&lt;/code&gt; and both containers will be stopped. This process is very easy to learn and to apply to any developer in your team and it just requires having Docker installed, no extra dependencies needed.&lt;/p&gt;

&lt;p&gt;It also has an extra advantage: a &lt;code&gt;Dockerfile&lt;/code&gt; could be created at the root of the project and then you will have a way of building and publishing an image of your application in your CI/CD service ready to deploy.&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%2Fg7ezp31kk9ijt1yticbn.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%2Fg7ezp31kk9ijt1yticbn.png" alt="Two Docker Containers with an App and a MySQL Database" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Installing the services directly on your machine has the problem of having a different environment for every developer in the project.&lt;/p&gt;

&lt;p&gt;This problem can be solved using Virtual Machines with the same environment for every member of the project, but it has the drawback of speed and resources needed when working with multiple projects.&lt;/p&gt;

&lt;p&gt;Using containers solves this problem by having just one Virtual Machine on your computer (or even zero if you are using Linux). Working with containers also prepares your application to be deployed in container-based services in staging and production environments, like Kubernetes.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Boosting Your Development Feedback Loop with Preview Environments</title>
      <dc:creator>Daniel Vigueras</dc:creator>
      <pubDate>Tue, 07 Jun 2022 09:20:28 +0000</pubDate>
      <link>https://forem.com/danielvigueras/boosting-your-development-feedback-loop-with-preview-environments-4a0m</link>
      <guid>https://forem.com/danielvigueras/boosting-your-development-feedback-loop-with-preview-environments-4a0m</guid>
      <description>&lt;h1&gt;
  
  
  Boosting Your Development Feedback Loop with Preview Environments
&lt;/h1&gt;

&lt;p&gt;The process of developing a new feature for a project usually involves the same steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;strong&gt;new branch&lt;/strong&gt; in the repository for the new feature (feature branch)&lt;/li&gt;
&lt;li&gt;Commit the code and create a &lt;strong&gt;new Pull Request&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review&lt;/strong&gt; the code changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge&lt;/strong&gt; the Pull Request into the base branch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt; the base branch to the Staging environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt; the feature (QA) in Staging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approve and deploy&lt;/strong&gt; to Production or &lt;strong&gt;request changes&lt;/strong&gt; opening the PR again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you follow &lt;em&gt;git-flow&lt;/em&gt; (or an alternative flow based on feature branches) this will sound familiar to you. The main point here is that until you don't deploy your new feature to the Staging environment it can't be tested in a realistic environment (local environments don't count 🙃 ).&lt;/p&gt;

&lt;p&gt;Also, this workflow leads to the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when the team is developing multiple features at the same time?&lt;/li&gt;
&lt;li&gt;Is the code in the base branch always deployable (in terms of business logic)?&lt;/li&gt;
&lt;li&gt;What if two new features are merged and tested but the team only wants to release one of them and delay the other one?&lt;/li&gt;
&lt;li&gt;Which feature is responsible for a bug found in Staging if two or more feature branches were merged into the base branch?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you imagine &lt;strong&gt;this way of working with branches and environments produces situations which would be better to avoid&lt;/strong&gt;. The solution is to implement &lt;strong&gt;Preview Environments&lt;/strong&gt; (sometimes also known as Preview Deployments).&lt;/p&gt;

&lt;p&gt;A Preview Environment allows us to have a new complete and isolated environment for each new feature. The advantages of having Preview Environments are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test features &lt;strong&gt;independently&lt;/strong&gt;: preview features&lt;/li&gt;
&lt;li&gt;Improves &lt;strong&gt;feedback loop&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid merging into the base branch&lt;/strong&gt; until the feature is approved&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid mixing new features&lt;/strong&gt; in the Staging environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview changes&lt;/strong&gt; as they would look in Production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following diagram reflects different environments: Production, Staging, and one Preview Environment for every active Pull Request&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%2Fvht4bnptj30cr6ynvui1.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%2Fvht4bnptj30cr6ynvui1.png" alt="Preview environments in a Project" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lifecycle
&lt;/h2&gt;

&lt;p&gt;Preview Environments are &lt;strong&gt;disposable environments&lt;/strong&gt;, which means that they are created, updated, and destroyed. They have the purpose of testing a feature, and &lt;strong&gt;once the task is done it doesn't make sense to keep them alive&lt;/strong&gt;. This process can take from minutes to months, it depends on the size of the feature, the time it takes to test it, if it makes sense to merge the Pull Request at the moment due to business requirements, etc.&lt;/p&gt;

&lt;p&gt;When a new Pull Request is created &lt;strong&gt;a new Preview Environment is created automatically&lt;/strong&gt; with the name of the Pull Request.&lt;/p&gt;

&lt;p&gt;If the Pull Request is updated with new commits then it is &lt;strong&gt;deployed again&lt;/strong&gt; to show the latest changes.&lt;/p&gt;

&lt;p&gt;When a Pull Request is merged or closed the environment &lt;strong&gt;is destroyed automatically&lt;/strong&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%2Fk0stuohelcslvmlf73zf.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%2Fk0stuohelcslvmlf73zf.png" alt="Preview environments lifecycle" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To be able to test a Preview Environment, every environment must have a unique preview URL. This URL can be automatically generated from the branch name. e.g.: for a branch named &lt;code&gt;feature/user-register&lt;/code&gt; in a project named &lt;code&gt;Manhattan&lt;/code&gt; the preview URL could be &lt;code&gt;https://feature-user-register.manhattan.com&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Data
&lt;/h2&gt;

&lt;p&gt;Another advantage of Preview Environments is that &lt;strong&gt;data can be seeded and updated without affecting other environments&lt;/strong&gt;. It means that every environment must create a new exclusive database when the environment is created, and delete the database once the environment is destroyed.&lt;/p&gt;

&lt;p&gt;The initial data of this database can be cloned from the Staging database or seeded with default values. &lt;strong&gt;This makes the data in this environment independent from other environments&lt;/strong&gt;, so QAs can create, update and delete anything they need without worrying about affecting other QAs testing other features. Also, the data in the environment can be reset to the initial values as needed.&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%2F15g47vim1oik06d4vx06.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%2F15g47vim1oik06d4vx06.png" alt="One database for every Preview Environment" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Dependency
&lt;/h2&gt;

&lt;p&gt;When we think of a new feature in a project we tend to think about a new branch in a repository, this can be usual in projects with a single repository and a single service, but this is not the common case in modern projects where multiple services are involved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A new feature could concern a backend service and a frontend service&lt;/strong&gt; at the same time. For the new feature, the frontend could depend on a new endpoint developed in the backend. Therefore we need a way to coordinate both repositories if we want to test the new feature. We can't just create a new Preview Environment for the frontend and then use the API available in the Staging environment because the new endpoints in the backend won't exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A good practice is to use the same branch name in both repositories&lt;/strong&gt;, e.g.: &lt;code&gt;feature/new-feature&lt;/code&gt;. Then we could deploy both backend and frontend to the same Preview Environment. By doing this we could be able to test the new feature completely and the frontend will use the backend endpoints of the new feature.&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%2Fh1kmzhu1u4h6onpulxwp.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%2Fh1kmzhu1u4h6onpulxwp.png" alt="Services in Preview Environments" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Environments
&lt;/h2&gt;

&lt;p&gt;Creating a Preview Environment can be done in many different ways. Creating the environment depends on the design of the project infrastructure and the way of deploying the application to Production. In this article, I am going to cover two options: deploying the app code directly to a server and using Docker containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy code to servers
&lt;/h3&gt;

&lt;p&gt;This process deploys the code to a server using methods like uploading the code using SFTP via SSH. As the environment doesn't exist it requires some steps before being able to deploy the code. Supposing we already have an existing server dedicated to Preview Environments, the step to create a new Environment would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install and set up&lt;/strong&gt; everything needed to run the code to be deployed: Web server, Database, Language Runtime, Virtual hosts, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create DNS records&lt;/strong&gt; for the preview URL(s)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy the code&lt;/strong&gt; using SFTP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way of creating Preview Environments has the following &lt;strong&gt;problems&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slow&lt;/strong&gt;: Installing and setting up services takes a lot of time. Also, if the code of the project has hundreds of files the deployment process will be slow too. We want to have these Preview Environments ready as soon as possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex&lt;/strong&gt;: There are a lot of steps to maintain. We need an orchestration tool (like Puppet, Chef, or Ansible) to execute all the steps required and maintain that code in the future.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error-prone&lt;/strong&gt;: Sometimes setting up a virtual host can fail due to a misconfiguration of the webserver or a repository with an external dependency can be down. If the process fails the environment won't be created.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Containers
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;much better alternative&lt;/strong&gt; is to use Docker containers. Supposing we already have an existing server dedicated to Preview Environments with Docker installed, the steps to create a new Environment would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create DNS records&lt;/strong&gt; for the preview URL(s)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the application container&lt;/strong&gt; in the CI/CD service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload the container&lt;/strong&gt; to a container image registry&lt;/li&gt;
&lt;li&gt;Connect to the server and &lt;strong&gt;run the Docker container&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process seems to be more agile than before, but there are still some questions to solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How are the Docker services exposed as external services? We would need something like a container to act as a reverse proxy with forwarding rules.&lt;/li&gt;
&lt;li&gt;How do we request the issue and manage the renovation of SSL certificates using &lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;Let's Encrypt&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;How many servers do we need for Preview Environments in our organization?&lt;/li&gt;
&lt;li&gt;How do projects map to Preview Environment servers in the organization?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It would be great if we could have a cluster of servers and we could forget about the specific server where the code must be deployed. If we need more resources we can just add more servers to the cluster. &lt;strong&gt;The best alternative to achieve this is to use a container scheduler like Kubernetes&lt;/strong&gt;. We just need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A Kubernetes cluster&lt;/strong&gt; in a provider (like DigitalOcean Kubernetes, Civo, Google GKE, Amazon EKS, etc)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write Dockerfiles&lt;/strong&gt; for every repository&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the Kubernetes manifests&lt;/strong&gt; to deploy every service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing Dockerfiles and Kubernetes manifest is out of the scope of this article.&lt;/p&gt;

&lt;p&gt;Instead of using a provider to launch a maintain your own Kubernetes cluster &lt;strong&gt;you can use existing services to achieve the same result&lt;/strong&gt;. One of my favorite options is &lt;a href="https://www.okteto.com/preview-environments/" rel="noopener noreferrer"&gt;Okteto&lt;/a&gt; &lt;strong&gt;Kubernetes service&lt;/strong&gt; and their feature to create Preview Environments from our code. This allows us to &lt;strong&gt;forget about creating and maintaining the Kubernetes cluster&lt;/strong&gt;, dealing with SSL certificates, and creating DNS records so that we can &lt;strong&gt;focus on what provides value for the project&lt;/strong&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Using only environments like Staging and Production is not a good idea&lt;/li&gt;
&lt;li&gt;Having an independent environment for every new feature improves the feedback loop of the team&lt;/li&gt;
&lt;li&gt;Getting feedback fast means you decrease the time needed to deploy a new feature to Production, and also the costs associated&lt;/li&gt;
&lt;li&gt;Using containers and schedulers like Kubernetes simplifies the creation of Preview Environments&lt;/li&gt;
&lt;li&gt;Working with external services like &lt;a href="https://www.okteto.com/preview-environments/" rel="noopener noreferrer"&gt;Okteto&lt;/a&gt; makes the whole process easier to create and maintain&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>testing</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
