<?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: Tobias Haindl</title>
    <description>The latest articles on DEV Community by Tobias Haindl (@tobhai).</description>
    <link>https://dev.to/tobhai</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%2F151106%2F0f5fa821-be41-437a-be3a-d3dc0546ea82.png</url>
      <title>DEV Community: Tobias Haindl</title>
      <link>https://dev.to/tobhai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tobhai"/>
    <language>en</language>
    <item>
      <title>Working Code Isn’t Enough</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Tue, 11 Jun 2024 17:33:00 +0000</pubDate>
      <link>https://dev.to/tobhai/working-code-isnt-enough-4mja</link>
      <guid>https://dev.to/tobhai/working-code-isnt-enough-4mja</guid>
      <description>&lt;p&gt;In &lt;em&gt;A Philosophy of Software Design&lt;/em&gt;, John Ousterhout argues that striving only for working code is not enough. The mindset of getting a feature done as fast as possible is not ideal, he calls this approach &lt;em&gt;Tactical Programming&lt;/em&gt;.&lt;br&gt;
Tactical programming results in addition of too much unnecessary complexity since not enough time is spent on coming up with a well-designed solution for your problem. This approach seems reasonable since some developers have to perform in high-pressure environments. With the next deadline lurking around the corner aiming for working code as quickly as possible seems like a good idea. Unfortunately, achieving working code in a short period of time often involves taking shortcuts. Each little shortcut increases the complexity of our code which harms the quality of our software systems in the long run.&lt;/p&gt;

&lt;p&gt;For Ousterhout the opposite of tactical programming is &lt;em&gt;Strategic Programming&lt;/em&gt; which requires more long-term thinking and a different mindset. A strategic developer doesn't sacrifice well-designed code for quicker delivery time. Ousterhout encourages us to come up with different solutions for a specific task and continue with the best design. Of course this approach costs more time but Ousterhout argues that it pays of in the long run and I think most of us would agree.&lt;br&gt;
Working in a well-designed codebase with fewer complexities enables us to not only develop new features faster but will also lead to fewer bugs in our software systems.&lt;/p&gt;

&lt;p&gt;Personally, I think it is very important to think about those two approaches when we are developing software. I believe that none of us adds complexity to a system on purpose but due to deadlines and external pressure we might tell ourselves that the little shortcuts we take are not too bad. Unfortunately, only time will reveal the amount of complexity caused by our shortcuts.&lt;br&gt;
Let’s fight off complexity and avoid taking too many shortcuts for faster delivery times.&lt;br&gt;
I'm convinced that I will pay off in the long run!&lt;/p&gt;

&lt;p&gt;Cover photo by GR Stockss on &lt;a href="https://unsplash.com/de/fotos/grayscale-photo-of-person-holding-glass-Iq9SaJezkOE" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>design</category>
      <category>career</category>
    </item>
    <item>
      <title>Bending Time in Spring Applications</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Mon, 08 May 2023 15:37:38 +0000</pubDate>
      <link>https://dev.to/tobhai/bending-time-in-spring-applications-33c8</link>
      <guid>https://dev.to/tobhai/bending-time-in-spring-applications-33c8</guid>
      <description>&lt;p&gt;Did you ever feel the need to unleash your inner Doctor Strange and manipulate time concisely when testing your Spring application?&lt;/p&gt;

&lt;p&gt;Let's check out the newest addition to the Spring ecosystem!&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Modulith
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Spring Modulith supports developers implementing logical modules in Spring Boot applications. It allows them to apply structural validation, document the module arrangement, run integration tests for individual modules, observe the modules' interaction at runtime and generally implement module interaction in a loosely-coupled way.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://docs.spring.io/spring-modulith/docs/0.6.0/reference/html/#fundamentals"&gt;Documentation&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article we will focus on one specific area of Modulith, the &lt;strong&gt;Moments&lt;/strong&gt; API.&lt;/p&gt;
&lt;h2&gt;
  
  
  Moments
&lt;/h2&gt;

&lt;p&gt;The Moments API enables developers to easily react to the passage of time-based events in your application. &lt;/p&gt;

&lt;p&gt;This allows you to easily write code which should be executed once a day, once a week etc.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example application
&lt;/h2&gt;

&lt;p&gt;I created a small sample application demonstrating features of the Moments API.&lt;br&gt;&lt;br&gt;
The code can be found on &lt;a href="https://github.com/tobHai/spring-modulith-samples"&gt;Github&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Let's walk through the application code together.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;First we need to add the Modulith dependencies to our Spring application. &lt;br&gt;
I'm using Maven to manage the dependencies in the sample application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.experimental&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-modulith-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;0.6.0&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the actual dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.experimental&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-modulith-moments&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, we also want to test our application code, so we will add the test dependency as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.experimental&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-modulith-starter-test&amp;lt;/artifactId&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Daily notifications
&lt;/h3&gt;

&lt;p&gt;In the example application we want to send out notifications daily to our customers. &lt;br&gt;
At the end of each day, we want to fetch all our customers and check if they opted in for receiving a notification on this day.&lt;/p&gt;

&lt;p&gt;Let's create a simple Spring service called &lt;code&gt;CustomerNotificationService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To listen to events emitted by Moments, we annotate a public method with &lt;code&gt;@EventListener&lt;/code&gt; and add the event we want to listen to as a method parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@EventListener
public void on(final DayHasPassed dayHasPassed) {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can easily implement our business logic and let Moments take care of the rest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@EventListener
public void on(final DayHasPassed dayHasPassed) {
  var passedDate = dayHasPassed.getDate();
  log.info("{} has passed. Checking notifications for customers.", passedDate);
  for (var customer : customerService.getCustomers()) {
    if (customer.allowedNotificationDays().contains(passedDate.getDayOfWeek())) {
      eventPublisher.publishEvent(new CustomerNotificationEvent(this, customer.id(), passedDate.getDayOfWeek()));
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For simplicity and brevity reasons the &lt;code&gt;CustomerService&lt;/code&gt; returns two hard-coded customers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class CustomerService {

  public Collection&amp;lt;Customer&amp;gt; getCustomers() {
    return List.of(new Customer(1, Set.of(DayOfWeek.MONDAY, DayOfWeek.FRIDAY)),
        new Customer(2, Set.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY)));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the customer opted in for receiving notifications on this day, we simply emit a new &lt;code&gt;CustomerNotificationEvent&lt;/code&gt; which could then be picked up by some other service and actual send out the notification. &lt;/p&gt;

&lt;p&gt;Since we care about our code, we want to ensure that it is working properly. Thankfully Modulith comes with great test support. &lt;br&gt;
Time for some time bending :)&lt;/p&gt;
&lt;h3&gt;
  
  
  Testing time
&lt;/h3&gt;

&lt;p&gt;The Moments API exposes a class called &lt;code&gt;TimeMachine&lt;/code&gt;. &lt;br&gt;
With it, we can easily manipulate time in our tests. &lt;br&gt;
In the following test we will enable the &lt;code&gt;TimeMachine&lt;/code&gt; by setting &lt;code&gt;spring.modulith.moments.enable-time-machine=true&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Additionally, we will annotate the test class with &lt;code&gt;@ApplicationModuleTest&lt;/code&gt;. This is another cool feature provided by Modulith. &lt;br&gt;
With it only beans defined in the given module (in our case &lt;code&gt;customer&lt;/code&gt;) will be created upon test execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ApplicationModuleTest
@Import(CustomerNotificationServiceTestConfig.class)
@TestPropertySource(properties = "spring.modulith.moments.enable-time-machine=true")
class CustomerNotificationServiceTest {

    private final TimeMachine timeMachine;

    CustomerNotificationServiceTest(final TimeMachine timeMachine) {
        this.timeMachine = timeMachine;
    }

    @Test
    void sendNotificationToCustomer(final PublishedEvents publishedEvents) {
        for (var i = 0; i &amp;lt; 7; i++) {
            timeMachine.shiftBy(Duration.ofDays(1));
        }

        assertThat(publishedEvents.ofType(CustomerNotificationEvent.class)
                .matching(e -&amp;gt; e.getCustomerId() == 1))
                .hasSize(2)
                .extracting(CustomerNotificationEvent::getDayOfWeek)
                .containsOnly(DayOfWeek.MONDAY, DayOfWeek.FRIDAY);

        assertThat(publishedEvents.ofType(CustomerNotificationEvent.class)
                .matching(e -&amp;gt; e.getCustomerId() == 2))
                .hasSize(2)
                .extracting(CustomerNotificationEvent::getDayOfWeek)
                .containsOnly(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY)
        ;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Bootstrapping with ApplicationModuleTest
&lt;/h4&gt;

&lt;p&gt;If we run the test and check the logs we can see what &lt;code&gt;@ApplicationModuleTest&lt;/code&gt; does under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bootstrapping @org.springframework.modulith.test.ApplicationModuleTest for Customer in mode STANDALONE (class dev.tobhai.modulithevents.ModulithEventsApplication)…

# Customer
&amp;gt; Logical name: customer
&amp;gt; Base package: dev.tobhai.modulithevents.customer
&amp;gt; Direct module dependencies: none
&amp;gt; Spring beans:
  + ….CustomerNotificationService
  + ….CustomerService
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the help of &lt;code&gt;@ApplicationModuleTest&lt;/code&gt; we can easily test the interaction between multiple beans defined in a module without the need for a full-blown Spring Context. &lt;/p&gt;

&lt;h4&gt;
  
  
  Shifting time in the test
&lt;/h4&gt;

&lt;p&gt;Now to the time-bending part of the test:&lt;br&gt;
First we define a fixed &lt;code&gt;Clock&lt;/code&gt; instance in the &lt;code&gt;CustomerNotificationServiceTestConfig&lt;/code&gt;.&lt;br&gt;
Then we enable the &lt;code&gt;TimeMachine&lt;/code&gt; by setting: &lt;code&gt;spring.modulith.moments.enable-time-machine&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we can simply inject the &lt;code&gt;TimeMachine&lt;/code&gt; instance into our test class and use it to shift time around in a very readable and concise way:&lt;br&gt;
&lt;code&gt;timeMachine.shiftBy(Duration.ofDays(1));&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Assertions on emitted events
&lt;/h4&gt;

&lt;p&gt;How can we actually verify that the &lt;code&gt;CustomerNotificationEvent&lt;/code&gt; was emitted properly?&lt;/p&gt;

&lt;p&gt;Modulith helps us out in this scenario as well:&lt;br&gt;
By adding &lt;code&gt;PublishedEvents publishedEvents&lt;/code&gt; to the signature of our test method, we can access all emitted events and perform assertions on them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;publishedEvents.ofType(CustomerNotificationEvent.class)&lt;br&gt;
                .matching(e -&amp;gt; e.getCustomerId() == 1)&lt;/code&gt; helps us to access all &lt;code&gt;CustomerNotificationEvent&lt;/code&gt;s for customer with ID. &lt;br&gt;
The time in our test starts on a Monday, and we shift "time" by seven days. &lt;br&gt;
Therefore, we expect that two events (for Monday and Friday) are emitted for customer 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this article we explored the Moments API of the Spring Modulith projects. &lt;br&gt;
Additionally, we had a look at testing support provided Modulith.&lt;/p&gt;

&lt;p&gt;Did you play around with Modulith yet? &lt;br&gt;
If so, what is your favorite feature?&lt;/p&gt;

&lt;p&gt;Feel free to follow for more Java related content :)&lt;/p&gt;

&lt;p&gt;Cover photo by Aron Visuals on &lt;a href="https://unsplash.com/photos/BXOXnQ26B7o"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Container Deep Dive 2: Container Engines</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Thu, 01 Dec 2022 17:35:14 +0000</pubDate>
      <link>https://dev.to/tobhai/container-deep-dive-2-container-engines-5bnh</link>
      <guid>https://dev.to/tobhai/container-deep-dive-2-container-engines-5bnh</guid>
      <description>&lt;p&gt;In the last post of this series we had a closer look at Container Runtimes.&lt;br&gt;&lt;br&gt;
However, for us developers interacting with Container Runtimes is tedious and not straightforward since they have not been designed to be consumed directly by the end-user.&lt;/p&gt;

&lt;p&gt;This issue is solved by Container Engines!&lt;br&gt;&lt;br&gt;
They provide convenient APIs for interacting with Container Runtimes without having to know all the low-level details.&lt;br&gt;&lt;br&gt;
Furthermore, this abstraction allows using &lt;strong&gt;different&lt;/strong&gt; Container Runtimes if necessary.&lt;/p&gt;

&lt;p&gt;If you started a container on your local machine once, you already interacted with a Container Engine since Docker is one of the most popular engines out there.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;docker&lt;/code&gt; Command Line Interface (CLI) allows us to interact with the heart of the Container engine.&lt;br&gt;&lt;br&gt;
In Docker terms called the Docker Daemon.&lt;br&gt;&lt;br&gt;
This CLI provides a high level of abstraction for the end-user since it focuses on the &lt;strong&gt;most&lt;/strong&gt; important parts of a container's life cycle.&lt;br&gt;&lt;br&gt;
The CLI allows us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start a new container&lt;/li&gt;
&lt;li&gt;stop a container&lt;/li&gt;
&lt;li&gt;restart a stopped container&lt;/li&gt;
&lt;li&gt;fetch a new image&lt;/li&gt;
&lt;li&gt;... and much more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on that brief overview of functionality we can extract the main responsibilities of a Container Engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Management of the container life cycle (forwarding proper commands to the underlying runtime)&lt;/li&gt;
&lt;li&gt;Pulling and pushing container images to image repositories&lt;/li&gt;
&lt;li&gt;Configuring storage for containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we are now more familiar with the functionalities provided by a Container Engine, let's have a closer look at two very popular engines:&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;The Docker Engine consists of the following parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dockerd&lt;/code&gt; a long-running daemon process which acts as server. Also called Docker daemon&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;docker&lt;/code&gt; CLI which acts as a client and communicates with &lt;code&gt;dockerd&lt;/code&gt; via the &lt;code&gt;Docker Engine API&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Docker daemon comes with it's one CLI called &lt;a href="https://docs.docker.com/engine/reference/commandline/dockerd/" rel="noopener noreferrer"&gt;&lt;code&gt;dockerd&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
It can be configured either by passing flags to &lt;code&gt;dockerd&lt;/code&gt; or a JSON configuration file.&lt;/p&gt;

&lt;p&gt;Once a request from the &lt;code&gt;Docker Engine API&lt;/code&gt; is received, the Docker daemon takes care of processing the request and reporting the status back to the &lt;code&gt;docker&lt;/code&gt; CLI.&lt;br&gt;&lt;br&gt;
Under the hood &lt;code&gt;containerd&lt;/code&gt; is used for interacting with the underlying Container runtime (in this case &lt;code&gt;runc&lt;/code&gt;).&lt;br&gt;&lt;br&gt;
So the Docker daemon does not interact with the Container runtime directly but makes use of another abstraction layer on top of &lt;code&gt;runc&lt;/code&gt;, called &lt;code&gt;containerd&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
If you haven't heard about Container Runtimes, check out the first article of this series: &lt;a href="https://dev.to/tobhai/container-deep-dive-part-1-container-runtime-4n1g"&gt;Container Runtimes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to tinker with the &lt;code&gt;Docker Engine API&lt;/code&gt; you can use the API directly via HTTP or integrate a provided SDK into your code.&lt;br&gt;&lt;br&gt;
See &lt;a href="https://docs.docker.com/engine/api/" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;The client-server architecture of Docker allows the &lt;code&gt;docker&lt;/code&gt; CLI to easily interact with remote Docker daemons as well. The Docker daemon process can listen to requests from the &lt;code&gt;Docker Engine API&lt;/code&gt; via three different socket types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unix&lt;/li&gt;
&lt;li&gt;fd&lt;/li&gt;
&lt;li&gt;tcp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TCP sockets enable the possibility for remote connections. The other two socket types are bound to the host and cannot be access from a remote machine.&lt;/p&gt;

&lt;p&gt;Docker is a prominent example of a Container Engine. It gained a lot of popularity since the initial release in 2013.&lt;br&gt;&lt;br&gt;
However, with the rise of Kubernetes another popular engine was born.&lt;br&gt;&lt;br&gt;
Let's have a look at &lt;code&gt;CRIO-O&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CRI-O
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;a href="http://cri-o.io/" rel="noopener noreferrer"&gt;CRI-O&lt;/a&gt; container engine provides a stable, more secure, and performant platform for running &lt;a href="https://www.opencontainers.org/" rel="noopener noreferrer"&gt;Open Container Initiative&lt;/a&gt; (OCI) compatible runtimes.&lt;br&gt;&lt;br&gt;
CRI-Os purpose is to be the container engine that implements the Kubernetes Container Runtime Interface (CRI) for OpenShift Container Platform and Kubernetes, replacing the Docker service.&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.openshift.com/container-platform/3.11/crio/crio_runtime.html" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The CRI-O container engine can launch a container by using an OCI-compliant Container Runtime like &lt;code&gt;runc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It uses &lt;code&gt;containers/image&lt;/code&gt; library to pull images from a registry. Therefore, it supports Docker schema2/version 1 and schema2/version2.&lt;/p&gt;

&lt;p&gt;Although it is not really necessary to interact with CRI-O directly, CRI-O comes with a bunch of CLIs and tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;crictl&lt;/code&gt;: allows debugging issue with the underlying runtime without having to set up other Kubernetes components&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runc&lt;/code&gt;: CLI for interacting directly with the runtime&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;podman&lt;/code&gt;: offers the same command-line features as the &lt;code&gt;docker&lt;/code&gt; CLI and additional features on top of it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;buildah&lt;/code&gt;: allows building of OCI images &lt;strong&gt;with&lt;/strong&gt; and &lt;strong&gt;without&lt;/strong&gt; a Dockerfile&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skopeo&lt;/code&gt;: helps with the management of container images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more information regarding the bundled tools see: &lt;a href="https://github.com/containers/buildah/tree/main/docs/containertools" rel="noopener noreferrer"&gt;CRI tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why was &lt;code&gt;CRI-O&lt;/code&gt; created?&lt;br&gt;&lt;br&gt;
&lt;code&gt;CRI-O&lt;/code&gt; was started by Red Hat, with the goal of decoupling Kubernetes from Docker.&lt;br&gt;&lt;br&gt;
It is now a project of the Cloud Native Computing Foundation.&lt;br&gt;&lt;br&gt;
With it, it is possible to use any OCI compliant Container Runtime in Kubernetes.&lt;br&gt;&lt;br&gt;
Since both &lt;code&gt;containerd&lt;/code&gt; and &lt;code&gt;CRI-O&lt;/code&gt; implement the Kubernetes Container Runtime Interface both can be used easily in Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Let's summarize what was covered in this article:&lt;/p&gt;

&lt;p&gt;First, we had a look at the responsibilities of a Container Engine.&lt;br&gt;&lt;br&gt;
Then we had a closer look at how the Docker Container Engine works under the hood.&lt;br&gt;&lt;br&gt;
Last but not least, &lt;code&gt;CRI-O&lt;/code&gt; was introduced as an alternative to Docker.&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@garett3?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Garett Mizunaka&lt;/a&gt; on &lt;a href="https://unsplash.com/@garett3?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>cli</category>
    </item>
    <item>
      <title>Container Deep Dive 1: Container Runtime</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Thu, 01 Dec 2022 17:33:43 +0000</pubDate>
      <link>https://dev.to/tobhai/container-deep-dive-part-1-container-runtime-3ng8</link>
      <guid>https://dev.to/tobhai/container-deep-dive-part-1-container-runtime-3ng8</guid>
      <description>&lt;p&gt;Containerization technologies stand upon the shoulders of giants.&lt;br&gt;&lt;br&gt;
Several building blocks are needed to make Docker, Kubernetes and co as powerful as they currently are.&lt;br&gt;&lt;br&gt;
In this series I want to explore each layer of those tools individually.&lt;br&gt;&lt;br&gt;
We will start at the bottom with Container Runtimes and move upwards to higher abstraction layers.&lt;br&gt;&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1667740596298%2Fszkz3Hvkb.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1667740596298%2Fszkz3Hvkb.png" alt="containerization.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Container Runtime
&lt;/h2&gt;

&lt;p&gt;A Container Runtime provides an execution environment for processes in an isolated manner (a container).&lt;br&gt;&lt;br&gt;
The most common Container Runtimes follow the Open Container Initiative (OCI) Runtime Specification.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the purpose of this specification? &lt;/li&gt;
&lt;li&gt;What does it include? &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Open Container Initiative Runtime Specification aims to specify the configuration, execution environment, and lifecycle of a container.&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/opencontainers/runtime-spec/blob/main/spec.md" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A detailed specification exists for different platforms (Linux, Windows etc.).&lt;br&gt;&lt;br&gt;
The Linux specification for example includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;available Namespaces&lt;/li&gt;
&lt;li&gt;available File systems&lt;/li&gt;
&lt;li&gt;available Devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's have a look at three prominent Container Runtimes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runc&lt;/li&gt;
&lt;li&gt;crun&lt;/li&gt;
&lt;li&gt;gVisor (runsc)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  runc
&lt;/h3&gt;

&lt;p&gt;Is a low level CLI capable of spawning and managing containers compliant with the OCI Runtime Specification.&lt;br&gt;&lt;br&gt;
It is written in Go and a project of the OCI.&lt;/p&gt;

&lt;p&gt;Since it takes care of low level interactions it is not recommended to use runc directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;unless there is some specific use case that prevents the use of tools like Docker or Podman, it is not recommended to use runc directly.&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/opencontainers/runc#using-runc" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So if you want to build the next Docker, feel free to use it directly :)&lt;/p&gt;

&lt;p&gt;runc is currently used by Docker as a default Container Runtime and can be used with Podman &amp;amp; Kubernetes as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  crun
&lt;/h3&gt;

&lt;p&gt;crun basically solves the same problem as runc: creation and management of the actual container instances.&lt;br&gt;&lt;br&gt;
Of course, it also fully implements the OCI Runtime Specification.&lt;br&gt;&lt;br&gt;
crun is written in C and promises a lower memory footprint and better performance. crun is used by default by Podman and can be used with Docker &amp;amp; Kubernetes as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  gVisor (runsc)
&lt;/h3&gt;

&lt;p&gt;gVisor is all about security. It includes a container runtime matching the OCI Runtime specification called runsc.&lt;br&gt;&lt;br&gt;
For improved security, containers created by gVisor are &lt;strong&gt;sandboxed&lt;/strong&gt; which leads to better isolation between your containers and the host system.&lt;br&gt;&lt;br&gt;
Only a limited surface of the host kernel is available for the container and calls to it are intercepted and monitored for suspicious activities.&lt;br&gt;&lt;br&gt;
gVisor is written in Go and maintained by Google.&lt;br&gt;&lt;br&gt;
runsc can be used with Docker &amp;amp; Kubernetes.&lt;/p&gt;

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

&lt;p&gt;In this article we had a look at the most basic building block of any modern Containerization tool. We briefly touched the Container Runtime Specification from the Open Container Initiative and compared three different implementations of the specification.&lt;/p&gt;

&lt;p&gt;In the next article we will explore Container Engines!&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@zuizuii?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Lucas Hoang&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/container?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>beginners</category>
      <category>devops</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Container Deep Dive 1: Container Runtime</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Sun, 06 Nov 2022 15:23:11 +0000</pubDate>
      <link>https://dev.to/tobhai/container-deep-dive-part-1-container-runtime-4n1g</link>
      <guid>https://dev.to/tobhai/container-deep-dive-part-1-container-runtime-4n1g</guid>
      <description>&lt;p&gt;Containerization technologies stand upon the shoulders of giants.&lt;br&gt;&lt;br&gt;
Several building blocks are needed to make Docker, Kubernetes and co as powerful as they currently are.&lt;br&gt;&lt;br&gt;
In this series I want to explore each layer of those tools individually.&lt;br&gt;&lt;br&gt;
We will start at the bottom with Container Runtimes and move upwards to higher abstraction layers.&lt;br&gt;&lt;br&gt;
 &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YQXEpAl5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1667740596298/szkz3Hvkb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YQXEpAl5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1667740596298/szkz3Hvkb.png" alt="containerization.png" width="541" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Container Runtime
&lt;/h1&gt;

&lt;p&gt;A Container Runtime provides an execution environment for processes in an isolated manner (a container).&lt;br&gt;&lt;br&gt;
The most common Container Runtimes follow the Open Container Initiative (OCI) Runtime Specification.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the purpose of this specification? &lt;/li&gt;
&lt;li&gt;What does it include? &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Open Container Initiative Runtime Specification aims to specify the configuration, execution environment, and lifecycle of a container.&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/opencontainers/runtime-spec/blob/main/spec.md"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A detailed specification exists for different platforms (Linux, Windows etc.).&lt;br&gt;&lt;br&gt;
The Linux specification for example includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;available Namespaces&lt;/li&gt;
&lt;li&gt;available File systems&lt;/li&gt;
&lt;li&gt;available Devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets have a look at three prominent Container Runtimes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runc&lt;/li&gt;
&lt;li&gt;crun&lt;/li&gt;
&lt;li&gt;gVisor (runsc)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  runc
&lt;/h2&gt;

&lt;p&gt;Is a low level CLI capable of spawning and managing containers compliant with the OCI Runtime Specification.&lt;br&gt;&lt;br&gt;
It is written in Go and a project of the OCI.&lt;/p&gt;

&lt;p&gt;Since it takes care of low level interactions it is not recommended to use runc directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;unless there is some specific use case that prevents the use of tools like Docker or Podman, it is not recommended to use runc directly.&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/opencontainers/runc#using-runc"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So if you want to build the next Docker, feel free to use it directly :)&lt;/p&gt;

&lt;p&gt;runc is currently used by Docker as a default Container Runtime and can be used with Podman &amp;amp; Kubernetes as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  crun
&lt;/h2&gt;

&lt;p&gt;crun basically solves the same problem as runc: creation and management of the actual container instances.&lt;br&gt;&lt;br&gt;
Of course it also fully implements the OCI Runtime Specification.&lt;br&gt;&lt;br&gt;
crun is written in C and promises a lower memory footprint and better performance. crun is used by default by Podman and can be used with Docker &amp;amp; Kubernetes as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  gVisor (runsc)
&lt;/h2&gt;

&lt;p&gt;gVisor is all about security. It includes a container runtime matching the OCI Runtime specification called runsc.&lt;br&gt;&lt;br&gt;
For improved security, containers created by gVisor are &lt;strong&gt;sandboxed&lt;/strong&gt; which leads to better isolation between your containers and the host system.&lt;br&gt;&lt;br&gt;
Only a limited surface of the host kernel is available for the container and calls to it are intercepted and monitored for suspicious activities.&lt;br&gt;&lt;br&gt;
gVisor is written in Go and maintained by Google.&lt;br&gt;&lt;br&gt;
runsc can be used with Docker &amp;amp; Kubernetes.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In this article we had a look at the most basic building block of any modern Containerization tool. We briefly touched the Container Runtime Specification from the Open Container Initiative and compared three different implementations of the specification.&lt;/p&gt;

&lt;p&gt;In the next article we will explore Container Engines!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>beginners</category>
      <category>container</category>
      <category>cncf</category>
    </item>
    <item>
      <title>Why I love IntelliJ's database window</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Fri, 28 Oct 2022 16:36:24 +0000</pubDate>
      <link>https://dev.to/tobhai/why-i-love-intellijs-database-window-44c3</link>
      <guid>https://dev.to/tobhai/why-i-love-intellijs-database-window-44c3</guid>
      <description>&lt;p&gt;In this article I want to share some of my favorite features of IntelliJ's database window/console.&lt;br&gt;&lt;br&gt;
Let's jump right in!&lt;/p&gt;

&lt;h1&gt;
  
  
  Find usage of tables
&lt;/h1&gt;

&lt;p&gt;One of my most used shortcuts is Find usage but it took me quite some time to figure out that this also works with database objects like tables:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661022982152%2Fm0-kuWqpv.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661022982152%2Fm0-kuWqpv.png" alt="usage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This makes refactoring your database easier since you can analyze the dependencies between your database objects and your source code quite easily with the help of Find usage&lt;/p&gt;

&lt;h1&gt;
  
  
  Multiple consoles
&lt;/h1&gt;

&lt;p&gt;In IntelliJ, it is possible to have multiple database consoles for a single data source.&lt;br&gt;&lt;br&gt;
All open consoles can be found under Scratches and Consoles in the side navigation:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661022989903%2FDd7CThSce.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661022989903%2FDd7CThSce.png" alt="consoles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, those console can be renamed like all the other files in your project!&lt;br&gt;&lt;br&gt;
For me this is especially helpful when I have to switch between different issues or tasks. With the help of multiple consoles I can keep all the queries needed for analyzing an issue together in one console and even give the console a telling name, so I can find those queries faster later on.&lt;/p&gt;

&lt;h1&gt;
  
  
  Safety features
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Preview delete
&lt;/h2&gt;

&lt;p&gt;Like in the normal source code editor ALT + ENTER is your best friend in the database console as well. With it, you can safely preview a delete query!&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661022999215%2Ffj0AO38k6.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661022999215%2Ffj0AO38k6.png" alt="preview-delete"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gives a preview of the affected row:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661023043742%2F6o9hchdhH.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661023043742%2F6o9hchdhH.png" alt="affected-row"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my opinion this makes removing data from your production database far more comfortable 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Warnings
&lt;/h2&gt;

&lt;p&gt;Tired of dropping all your data due to a missing &lt;code&gt;where&lt;/code&gt; condition in your delete statement? IntelliJ issues a warning for unsafe statement and requires extra confirmation before executing such statements.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1666975509537%2FkIcd9Werh.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1666975509537%2FkIcd9Werh.png" alt="image.png"&gt;&lt;/a&gt;&lt;br&gt;
Quite helpful in my opinion! This also works for update statements:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1666975492087%2Fj17aB9Q5_.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1666975492087%2Fj17aB9Q5_.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Auto-expand column names
&lt;/h1&gt;

&lt;p&gt;Last but not least, auto expansion!&lt;br&gt;&lt;br&gt;
Lets say you want to select &lt;strong&gt;nearly&lt;/strong&gt; all of your columns but do not want to type all column names manually.&lt;br&gt;&lt;br&gt;
In my opinion the easiest way to achieve this is by using the expand column list feature. Simply write: &lt;code&gt;SELECT * FROM &amp;lt;your_table&amp;gt;&lt;/code&gt; highlight the asterisk character and press ALT + ENTER. This will give you all the column names, afterwards you can delete any unwanted column!&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661023063535%2FCHiZiSDjK.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1661023063535%2FCHiZiSDjK.gif" alt="expand-column-list.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrap up
&lt;/h1&gt;

&lt;p&gt;That's all from my side for now.&lt;br&gt;&lt;br&gt;
What do you think about the mentioned features?&lt;br&gt;&lt;br&gt;
Did I miss a feature you love?&lt;br&gt;&lt;br&gt;
Let me know in the comments!&lt;/p&gt;

</description>
      <category>java</category>
      <category>intellij</category>
      <category>beginners</category>
      <category>database</category>
    </item>
    <item>
      <title>Building Flutter Linux on Manjaro</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Sat, 06 Aug 2022 08:56:29 +0000</pubDate>
      <link>https://dev.to/tobhai/building-flutter-linux-on-manjaro-1kd6</link>
      <guid>https://dev.to/tobhai/building-flutter-linux-on-manjaro-1kd6</guid>
      <description>&lt;p&gt;Last week I was eager to check out a little Flutter application built by &lt;a href="https://hashnode.com/@internerd"&gt;Lucas Schuster&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
I was especially interested in trying the Flutter Desktop support for Linux.&lt;br&gt;&lt;br&gt;
After installing Flutter via &lt;a href="https://github.com/fluttertools/fvm"&gt;fvm&lt;/a&gt;, I checked out his Git repository and issued the following command for building the Flutter app on Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flutter build linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately this gave me the following error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:605 (message):
A required package was not found
Call Stack (most recent call first):
/usr/share/cmake/Modules/FindPkgConfig.cmake:827 (_pkg_check_modules_internal)
flutter/CMakeLists.txt:25 (pkg_check_modules)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I doubled checked the &lt;a href="https://docs.flutter.dev/development/platform-integration/desktop"&gt;official documentation&lt;/a&gt; and verified that the following packages were installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clang &lt;/li&gt;
&lt;li&gt;cmake &lt;/li&gt;
&lt;li&gt;ninja-build &lt;/li&gt;
&lt;li&gt;pkg-config &lt;/li&gt;
&lt;li&gt;gtk3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Afterwards I reran the build command but with the verbose flag enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flutter build linux -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which resulted in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[] -- Checking for module 'gtk+-3.0'
[+1 ms] -- No package 'gtk+-3.0' found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flutter was not able to locate my gtk installation.&lt;br&gt;&lt;br&gt;
You can check if &lt;code&gt;pkg-config&lt;/code&gt; can locate the package by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkg-config --libs gtk+-3.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command returned the following error on my system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Package gtk+-3.0 was not found in the pkg-config search path.
Perhaps you should add the directory containing `gtk+-3.0.pc'
to the PKG_CONFIG_PATH environment variable
No package 'gtk+-3.0' found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I fixed this by figuring out where &lt;code&gt;gtk3&lt;/code&gt; is installed and adding the directory to the &lt;code&gt;PKG_CONFIG_PATH&lt;/code&gt; variable.&lt;br&gt;&lt;br&gt;
Check where the package is located:&lt;br&gt;
&lt;/p&gt;

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

gtk3 /usr/lib/pkgconfig/gtk+-3.0.pc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add this directory to &lt;code&gt;PKG_CONFIG_PATH&lt;/code&gt; environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export PKG_CONFIG_PATH=/usr/lib/pkgconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might want to add this to your &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; config file.&lt;br&gt;&lt;br&gt;
Don't forget to reload your environment, e.g. with the &lt;code&gt;source ~/.zshrc&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;After modifying the &lt;code&gt;PKG_CONFIG_PATH&lt;/code&gt; the Flutter build returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Package 'shared-mime-info', required by 'gdk-pixbuf-2.0', not found
Configuring incomplete, errors occurred!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, another familiar error, let's check if &lt;code&gt;shared-mime-info&lt;/code&gt; is available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkg-config --libs shared-mime-info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the package was not found on your system, you can install it via &lt;code&gt;yay&lt;/code&gt; or &lt;code&gt;pacman&lt;/code&gt;&lt;br&gt;&lt;br&gt;
E.g.:&lt;code&gt;yay -Sy shared-mime-info&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Afterwards check the installation path:&lt;code&gt;yay -Ql shared-mime-info&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Search for the directory including the &lt;code&gt;shared-mime-info.pc&lt;/code&gt; file, in my case it was: &lt;code&gt;/usr/share/pkgconfig/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Therefore, I added this directory to the &lt;code&gt;PKG_CONFIG_PATH&lt;/code&gt; as well:&lt;br&gt;&lt;br&gt;
&lt;code&gt;export PKG_CONFIG_PATH=/usr/lib/pkgconfig/:/usr/share/pkgconfig/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Afterwards I was able to build and run the Flutter application on Manjaro!&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@danist07?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Danist Soh&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/build?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>beginners</category>
      <category>manjaro</category>
      <category>linux</category>
    </item>
    <item>
      <title>Analyze your coding activity in a privacy-friendly way</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Thu, 23 Jun 2022 07:02:35 +0000</pubDate>
      <link>https://dev.to/tobhai/analyze-your-coding-activity-in-a-privacy-friendly-way-3p0b</link>
      <guid>https://dev.to/tobhai/analyze-your-coding-activity-in-a-privacy-friendly-way-3p0b</guid>
      <description>&lt;p&gt;These days data is ubiquitous, we generate loads of data each day, consciously and unconsciously.&lt;br&gt;&lt;br&gt;
I'm not the biggest fan of giving away my personal data, but I still like to analyze my spent time by tracking some stats. This also applies for my coding activity.&lt;br&gt;&lt;br&gt;
That's why I was looking for an open source tools which helps with tracking and analyzing my coding time.&lt;br&gt;&lt;br&gt;
I found a great open-source, privacy-friendly tool called Wakapi which can help with those tasks.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;a href="https://github.com/muety/wakapi" rel="noopener noreferrer"&gt;Wakapi&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Wakapi is a Wakatime compatible open-source backend for collecting and visualizing coding statistics. It can be self-hosted fairly easily be running the provided Docker image.&lt;br&gt;&lt;br&gt;
The compatibility with Wakatime is a big bonus since Wakatime provides many plugins for data collection from your favorite IDE or text editor. I'm pretty sure you can find an integration for your most loved tool &lt;a href="https://wakatime.com/plugins" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Let's see how one can run Wakapi with Docker.&lt;/p&gt;
&lt;h3&gt;
  
  
  Running with Docker
&lt;/h3&gt;

&lt;p&gt;First we will create a Docker volume to make sure our data is not lost when the container is stopped/removed:&lt;br&gt;&lt;br&gt;
&lt;code&gt;docker volume create wakapi-data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To make sure user passwords are hashed properly we generate a salt:&lt;br&gt;&lt;br&gt;
&lt;code&gt;SALT="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w ${1:-32} | head -n 1)"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then we run the Docker image:&lt;br&gt;&lt;br&gt;
&lt;code&gt;docker run -d \&lt;br&gt;
  -p 3000:3000 \&lt;br&gt;
  -e "WAKAPI_PASSWORD_SALT=$SALT" \&lt;br&gt;
  -v wakapi-data:/data \&lt;br&gt;
  --name wakapi \&lt;br&gt;
  ghcr.io/muety/wakapi:latest&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/muety/wakapi" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Create account
&lt;/h3&gt;

&lt;p&gt;After the Docker container is up and running, we need to create an admin account.&lt;br&gt;&lt;br&gt;
Navigate to your Wakapi frontend e.g. if run locally &lt;a href="http://localhost:3000/login" rel="noopener noreferrer"&gt;http://localhost:3000/login&lt;/a&gt; and sign up there.&lt;br&gt;&lt;br&gt;
The first registered account will become the admin account for this Wakapi instance. After you are logged in you can obtain your personal API key in the top right corner: &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%2Fua33fmc88idikvugqiid.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%2Fua33fmc88idikvugqiid.png" alt="Wakapi API key"&gt;&lt;/a&gt; (Don't worry the API key in the screenshot is from a local instance ;))&lt;br&gt;&lt;br&gt;
If you do not want any other users to create an account on your instance, you can set &lt;code&gt;WAKAPI_ALLOW_SIGNUP&lt;/code&gt; environment variable to false.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure the plugin
&lt;/h3&gt;

&lt;p&gt;After the account creation is done, you need to install the Wakatime plugin for your desired IDE. Once the installation is done you need to configure it to use your own Wakapi instance by creating a config file under &lt;code&gt;~/.wakatime.cfg&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

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

# Your Wakapi server URL
api_url = http://localhost:3000/api/heartbeat

# Wakapi API key, obtained from the web interface in the previous step
api_key = 406fe41f-6d69-4183-a4cc-121e0c524c2b

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Analyze your activity
&lt;/h3&gt;

&lt;p&gt;After the Wakatime plugin is configured properly, it should send data from your IDE/editor to your Wakapi backend where your statistics are waiting for you to be analyzed.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1655896375609%2FaSIjMlR43.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1655896375609%2FaSIjMlR43.png" alt="wakapi.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wakapi provides filters for different time-windows and statistics for editor usage, time spent on projects, time spent coding in a certain language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap up
&lt;/h3&gt;

&lt;p&gt;I think it is quite useful to visualize your coding activity. It provides insights into how you spend your days and might help optimizing your daily schedule.&lt;/p&gt;

&lt;p&gt;Shout-out to &lt;a href="https://muetsch.io/" rel="noopener noreferrer"&gt;Ferdinand Mtsch&lt;/a&gt; for creating this amazing open-source tool and thanks to &lt;a href="https://wakatime.com/" rel="noopener noreferrer"&gt;Wakatime&lt;/a&gt; for open-sourcing their plugins.&lt;/p&gt;

&lt;p&gt;Header image by &lt;a href="https://unsplash.com/@isaacmsmith" rel="noopener noreferrer"&gt;Isaac Smith&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>productivity</category>
      <category>privacy</category>
    </item>
    <item>
      <title>What do we say to typos? Not today!</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Wed, 20 Apr 2022 17:55:14 +0000</pubDate>
      <link>https://dev.to/tobhai/what-do-we-say-to-typos-not-today-19hj</link>
      <guid>https://dev.to/tobhai/what-do-we-say-to-typos-not-today-19hj</guid>
      <description>&lt;p&gt;As we all know, typing frantically can lead to typos quite easily. &lt;/p&gt;

&lt;p&gt;Since I'm not really proud of all the typos I produce during a normal work day, I decided to do something against it a few &lt;br&gt;
weeks ago.&lt;/p&gt;
&lt;h2&gt;
  
  
  LanguageTool to the rescue
&lt;/h2&gt;

&lt;p&gt;I was already used to wiggly lines in my favorite IDE IntelliJ and really missed the spell and grammar check capabilities in other editors especially when writing something in the browser.&lt;br&gt;
A colleague told me that IntelliJ is using &lt;a href="https://languagetool.org/de" rel="noopener noreferrer"&gt;LanguageTool&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Therefore, I looked around on GitHub for a way of hosting my own LanguageTool server. &lt;br&gt;
I came across &lt;a href="https://github.com/Erikvl87/docker-languagetool" rel="noopener noreferrer"&gt;this repository&lt;/a&gt; and decided to give it a go and run it on my Linux server.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running your own instance with Docker
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Setup n-gram support
&lt;/h3&gt;

&lt;p&gt;For better analysis results we will download the n-gram dataset provided by LanguageTool.&lt;br&gt;
N-grams can be used to distinguish between words which are easily confused like &lt;code&gt;there&lt;/code&gt; and &lt;code&gt;their&lt;/code&gt; or &lt;code&gt;bite&lt;/code&gt; and &lt;code&gt;byte&lt;/code&gt;. Depending on the context LanguageTool can make assumptions about the correct word with the help of the n-gram files.&lt;/p&gt;

&lt;p&gt;To download all English n-grams in your current directory, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget https://languagetool.org/download/ngram-data/ngrams-en-20150817.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To unpack the downloaded zip file into the directory &lt;code&gt;ngrams&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unzip ngrams-en-20150817.zip -d ngrams
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you want to add n-grams for an additional language, make sure to adhere to the directory structure.&lt;br&gt;&lt;br&gt;
E.g. for English and German:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrams/en/1grams
ngrams/en/2grams
ngrams/en/3grams
ngrams/de/1grams
ngrams/de/2grams
ngrams/de/3grams
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Pull and run the image
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull erikvl87/languagetool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To pull the latest version from DockerHub.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -e langtool_languageModel=/ngrams -v /home/&amp;lt;insert-your-path-here&amp;gt;/ngrams:/ngrams -p 8010:8010 erikvl87/languagetool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To run the LanguageTool server with n-gram analysis enabled.&lt;br&gt;
Make sure to change &lt;code&gt;&amp;lt;insert-your-path-here&amp;gt;/ngrams&lt;/code&gt; to your local &lt;code&gt;ngrams&lt;/code&gt; directory which we previously created.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install the Chrome plugin
&lt;/h3&gt;

&lt;p&gt;To enable spell and grammar checking in Chrome install the corresponding &lt;a href="https://chrome.google.com/webstore/detail/grammar-spell-checker-%E2%80%94-l/oldceeleldhonbafppcapldpdifcinji" rel="noopener noreferrer"&gt;plugin&lt;/a&gt;.&lt;br&gt;
Head to the setting and set to URL to your LanguageTool Server:&lt;br&gt;&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%2Fr4sevpd1ozfrtruq025c.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%2Fr4sevpd1ozfrtruq025c.png" alt="LanguageTool configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is important to add &lt;code&gt;/v2&lt;/code&gt; to the base path, otherwise the Chrome plugin won't work!&lt;br&gt;&lt;br&gt;
E.g. if your LanguageTool server is reachable under &lt;code&gt;https://my-domain.dev&lt;/code&gt; the configured URL should be &lt;code&gt;https:/my-domain.dev/v2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After that the LanguageTool should be available to you.&lt;br&gt;
In the &lt;code&gt;dev.to&lt;/code&gt; editor it looks like this:&lt;br&gt;&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%2F7z6sql4g04cdwnwn3ujx.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%2F7z6sql4g04cdwnwn3ujx.png" alt="LanguageTool integration dev.to"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additional plugins can be found &lt;a href="https://languagetool.org/de" rel="noopener noreferrer"&gt;here&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;That's all from me! &lt;br&gt;
Thanks for reading! &lt;br&gt;
Happy spell and grammar checking everyone!&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__151106"&gt;
    &lt;a href="/tobhai" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&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%2Fuser%2Fprofile_image%2F151106%2F0f5fa821-be41-437a-be3a-d3dc0546ea82.png" alt="tobhai image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/tobhai"&gt;Tobias Haindl&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/tobhai"&gt;Jack of all trades (Full-stack engineer).  
Navigating my way through the world of tech.    
Writing about my journey here.    
Focusing on Java, Databases and Python.  &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Header image by &lt;a href="https://unsplash.com/@patrickian4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Patrick Fore&lt;/a&gt;&lt;/p&gt;

</description>
      <category>writing</category>
      <category>docker</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Effective Remote Work</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Sun, 10 Apr 2022 18:09:14 +0000</pubDate>
      <link>https://dev.to/tobhai/effective-remote-work-1cic</link>
      <guid>https://dev.to/tobhai/effective-remote-work-1cic</guid>
      <description>&lt;p&gt;In the last years remote work became necessary for many of us. &lt;br&gt;
I do not think I need to tell you about the reason behind that.&lt;/p&gt;

&lt;p&gt;Since the first lockdown in Austria about two years ago, I was working remotely most of the time.&lt;br&gt;&lt;br&gt;
Therefore, I spend some time thinking and reading about remote work. &lt;br&gt;
One of my favorite books about it is &lt;a href="https://pragprog.com/titles/jsrw/effective-remote-work/" rel="noopener noreferrer"&gt;&lt;em&gt;Effective Remote Work&lt;/em&gt;&lt;/a&gt; by James Stanier.&lt;br&gt;
In this post I want to share my personal key takeaways from it with you. &lt;/p&gt;
&lt;h2&gt;
  
  
  Checkpoints
&lt;/h2&gt;

&lt;p&gt;During a “normal” work schedule which includes a commute to the office, the work day has naturally checkpoints built into it.&lt;/p&gt;

&lt;p&gt;You leave the house at a certain time, you might take the train to another city or into the city center. After your commute is finished, and you arrived at the office your brain switches into work mode automatically.  &lt;/p&gt;

&lt;p&gt;At midday, you promised Alice to grab some lunch at 12. &lt;br&gt;
Another checkpoint helping your brain switch into a more relaxed state, and it is way easier to unglue your eyes from the screen if you made an arrangement already.  &lt;/p&gt;

&lt;p&gt;At the end of the day you &lt;strong&gt;physically&lt;/strong&gt; leave the office.&lt;br&gt;
Work related thoughts can be left behind as well and your brain can switch into leisure mode easily.&lt;/p&gt;

&lt;p&gt;Those checkpoints are essential for your mental health and your energy levels. &lt;br&gt;
They help your brain transition between different states and help with unplugging from a potential stressful day.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Working remotely means that you are inviting all of the negative parts of work into your home.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;P.28 Effective Remote Work, James Stanier&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sometimes work causes frustration and like James Stanier put it in the quote &lt;em&gt;you are inviting all of the negative parts of work into your home.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Therefore, it might help to install a &lt;strong&gt;shutdown routine&lt;/strong&gt; as the last checkpoint of your work day.&lt;br&gt;&lt;br&gt;
This routine can be manifold. &lt;br&gt;
You might want to go for a short walk around your neighborhood after closing your laptop. &lt;br&gt;
Or you can try &lt;a href="https://www.calnewport.com/blog/2009/06/08/drastically-reduce-stress-with-a-work-shutdown-ritual/" rel="noopener noreferrer"&gt;Cal Newport’s shutdown ritual&lt;/a&gt; which even includes a cool-sounding shutdown phrase.&lt;/p&gt;

&lt;p&gt;Whatever action you prefer, I think it is essential to show our mind that work is over now and leisure mode can be activated by performing a similar routine at the end of each work day.&lt;/p&gt;
&lt;h2&gt;
  
  
  Artifacts - the backbone of remote working
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Artifacts are the backbone of remote working. Creating effective ones is an art, just like programming.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;P.101 Effective Remote Work, James Stanier&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;James Stanier points out that written word is the optimal medium when it comes to information consumption. &lt;br&gt;
Several types of &lt;em&gt;Written Artifacts&lt;/em&gt; are helpful in a remote work setting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design documents&lt;/li&gt;
&lt;li&gt;Meeting minutes&lt;/li&gt;
&lt;li&gt;Newsletters&lt;/li&gt;
&lt;li&gt;Wikis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I really like the idea of using an internal newsletter (e.g. team or product related) to provide information to interested people. &lt;br&gt;
It is an easy way of sharing the work your team has already finished or is planning to do soon.&lt;/p&gt;

&lt;p&gt;Another category of artifacts we encounter in our daily jobs are &lt;em&gt;Codebase Artifacts&lt;/em&gt; like Commit messages, Pull Requests and Architecture Design Records.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://adr.github.io/" rel="noopener noreferrer"&gt;Architecture Design Records&lt;/a&gt; (ADR) are documents describing a design choice. These documents should help with capture the reasoning behind a design decision. They should be checked in into your version control system, so they are easily accessible for any developer.&lt;/p&gt;

&lt;p&gt;To me ADRs look very helpful - unfortunately I have not encountered any in my work projects yet.&lt;/p&gt;
&lt;h2&gt;
  
  
  Measure twice, cut once
&lt;/h2&gt;

&lt;p&gt;We can apply this old proverb originating from carpentry and similar traditional crafts to our daily communication. &lt;br&gt;
When working with an expensive piece of wood, you want to make sure all your measurements are correct before cutting through it.&lt;br&gt;
Therefore, you should measure twice, cut once.&lt;/p&gt;

&lt;p&gt;Especially in asynchronous communication time is *&lt;em&gt;not *&lt;/em&gt; critical. &lt;br&gt;
We can invest a few extra minutes to check if the chat message is easily comprehensible or not. &lt;br&gt;
Before sending, take another look.&lt;br&gt;&lt;br&gt;
Read over it.&lt;br&gt;&lt;br&gt;
Check if the written words are well-structured and convey the intended message properly. &lt;/p&gt;

&lt;p&gt;The sender should &lt;strong&gt;optimize&lt;/strong&gt; and make sure that the receiver can easily understand the message! &lt;/p&gt;
&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Built **checkpoints **into your remote work day, to help your brain transition from leisure mode into work mode and vice versa.&lt;/li&gt;
&lt;li&gt;Master the production of different kind of &lt;strong&gt;artifacts&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Do not forget to &lt;em&gt;measure twice, cut once&lt;/em&gt; when communicating with your peers! Review your written messages like you review code from others :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all from me for now, thanks for reading!&lt;br&gt;
For more content on software development feel free to follow me. &lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__151106"&gt;
    &lt;a href="/tobhai" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&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%2Fuser%2Fprofile_image%2F151106%2F0f5fa821-be41-437a-be3a-d3dc0546ea82.png" alt="tobhai image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/tobhai"&gt;Tobias Haindl&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/tobhai"&gt;Jack of all trades (Full-stack engineer).  
Navigating my way through the world of tech.    
Writing about my journey here.    
Focusing on Java, Databases and Python.  &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@avirichards?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Avi Richards&lt;/a&gt; on Unsplash&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>books</category>
      <category>remote</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Forget the 10.000 hour rule! Strive for range!</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Sun, 27 Mar 2022 17:19:14 +0000</pubDate>
      <link>https://dev.to/tobhai/forget-the-10000-hour-rule-strive-for-range-20i3</link>
      <guid>https://dev.to/tobhai/forget-the-10000-hour-rule-strive-for-range-20i3</guid>
      <description>&lt;p&gt;In his book &lt;em&gt;Outliers&lt;/em&gt; Malcom Gladwell argues that the perfection of a certain skill takes a lot of time. &lt;br&gt;
He brings up successful people from different domains and analyzes their lives.&lt;br&gt;
It all boils down to one thing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;practice&lt;/li&gt;
&lt;li&gt;more practice &lt;/li&gt;
&lt;li&gt;even more practice&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Range
&lt;/h2&gt;

&lt;p&gt;David Epstein in his book &lt;em&gt;Range&lt;/em&gt; provides a fresh perspective on this debate.&lt;br&gt;
Who has the upper hand in long and daunting journey striving for perfection in a certain skill:&lt;br&gt;
the one who focuses early and gets in a lot of practice ? &lt;br&gt;
Or the one trying out things across different fields and deciding later on what to master in life.&lt;br&gt;
He brings up famous people like Vincent van Gogh and explains how a late start and a huge range of different experiences can be essential for mastering a skill.&lt;/p&gt;

&lt;p&gt;During reading his book, I thought about how this discussion influences tech careers. &lt;br&gt;
I think a late start into a tech career does not prevent one from becoming a great developer. &lt;br&gt;
Earlier experiences in other domains can help with understanding the customer/user better.&lt;br&gt;
Previously earned domain knowledge can be applied during requirements engineering. It can help explain the customers’ needs to colleagues etc.&lt;/p&gt;

&lt;p&gt;Of course there are a lot of hard skills to master in tech like getting familiar with the syntax of a new programming language. &lt;br&gt;
However, I think a broad range of skills can be helpful, especially when working in a team.&lt;br&gt;
Having people with different skills on your team brings fresh and new perspective to your hive mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's discuss
&lt;/h2&gt;

&lt;p&gt;Did you specialize early in your career on a certain niche?&lt;br&gt;
Did you switch careers late? &lt;br&gt;
If so, do you regret it or are your previously earned experiences beneficial?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.gladwellbooks.com/titles/malcolm-gladwell/outliers/9780316040341/"&gt;Outliers&lt;/a&gt;&lt;br&gt;
&lt;a href="https://davidepstein.com/the-range/"&gt;Range&lt;/a&gt;&lt;br&gt;
&lt;a href="https://unsplash.com/photos/PxiAc1aElFQ"&gt;Header picture&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>discuss</category>
      <category>career</category>
      <category>books</category>
    </item>
    <item>
      <title>Intro to Postgres enums</title>
      <dc:creator>Tobias Haindl</dc:creator>
      <pubDate>Mon, 07 Mar 2022 16:31:44 +0000</pubDate>
      <link>https://dev.to/tobhai/intro-to-postgres-enums-31ip</link>
      <guid>https://dev.to/tobhai/intro-to-postgres-enums-31ip</guid>
      <description>&lt;p&gt;In most programming languages the use of enums is pretty common and heavily relied upon by developers. It took me quite some time to learn that this feature exists in the database realm as well. &lt;br&gt;
Therefore, I will provide a short introductory summary of enumerated types in Postgres.&lt;/p&gt;

&lt;p&gt;Enums are used for representing a limited number of possible values. &lt;br&gt;
Status fields are a good example.&lt;br&gt;
Let’s say we want to store account information inside a database table:&lt;/p&gt;

&lt;p&gt;In our example an account can have one of the following status:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ACTIVE&lt;/li&gt;
&lt;li&gt;DELETED&lt;/li&gt;
&lt;li&gt;BANNED&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can create a database enum by executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT_STATUS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ACTIVE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DELETED'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'BANNED'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then use the generated enum in our table definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT_STATUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;USERNAME&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EMAIL&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we insert a new account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ACTIVE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'testuser'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'user@test.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we set the datatype of the STATUS table to ACCOUNT_STATUS the database now takes care of validation.&lt;/p&gt;

&lt;p&gt;If we try to insert an account with status  INACTIVE&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INACTIVE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'testuser'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'user@test.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database returns the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="k"&gt;input&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;account_status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"INACTIVE"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since "INACTIVE" is not valid for our defined enum.&lt;/p&gt;

&lt;p&gt;Having a data validation mechanism like this on database level helps to enforce data integrity at all times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying enums
&lt;/h2&gt;

&lt;p&gt;Unfortunately the values of enum types can &lt;strong&gt;not&lt;/strong&gt; be retrieved by the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT_STATUS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But one can make use of the built-in functions &lt;code&gt;UNNEST&lt;/code&gt; and &lt;code&gt;ENUM_RANGE&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;UNNEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENUM_RANGE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ACCOUNT_STATUS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query returns one row for each enum value. &lt;br&gt;
In our example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ACTIVE&lt;/li&gt;
&lt;li&gt;INACTIVE&lt;/li&gt;
&lt;li&gt;BANNED&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Alternatives to enums: check constraints
&lt;/h2&gt;

&lt;p&gt;We can add a similar data validation mechanism to our database tables by adding a check constraint.&lt;br&gt;
In our example the constraint would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ACTIVE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'INACTIVE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'BANNED'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;USERNAME&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EMAIL&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After defining the column name and type we add the constraint by using the &lt;code&gt;CHECK&lt;/code&gt; keyword. &lt;br&gt;
In the parenthesis following &lt;code&gt;CHECK&lt;/code&gt; we define a boolean expression which must be true for every row of the table.&lt;br&gt;
For better maintainability we can also give the check constraint a name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT_STATUS_CONSTRAINT&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ACTIVE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'INACTIVE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'BANNED'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;USERNAME&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EMAIL&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enum vs Check constraint
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Advantages of enums&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once defined enums can be reused in different columns or tables.&lt;br&gt;
Additionally, the usage of enums helps with understanding the database schema.&lt;br&gt;
Because creation of a specific type conveys more purpose: e.g. column of type &lt;code&gt;ACCOUNT_STATUS&lt;/code&gt; is more specific than a column of type &lt;code&gt;TEXT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages of enums&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With enums we defined a new data type for our column (note the &lt;code&gt;CREATE TYPE&lt;/code&gt; syntax above).&lt;br&gt;
Therefore, the built-in string operators and functions do &lt;strong&gt;not&lt;/strong&gt; work on enums.&lt;br&gt;
With a check constraint do not have to define our own data type, so any built-in operator and function for the used column data type can be used.&lt;/p&gt;

&lt;p&gt;Like in other parts of software development, designing a database schema involves tradeoffs. &lt;br&gt;
So it is quite handy to have different tools in your tool belt!&lt;/p&gt;

&lt;p&gt;If you like this summary, feel free to follow me on Twitter or here for more content.&lt;/p&gt;

&lt;p&gt;Sources:&lt;br&gt;
&lt;a href="https://www.postgresql.org/docs/9.1/datatype-enum.html"&gt;Postgres docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>postgres</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
