<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Devmystify Blog</title>
    <link>https://devmystify.com</link>
    <description>Latest updates and insights from Devmystify</description>
    <language>en</language>
    
      <item>
        <title>test post !!!</title>
        <link>https://devmystify.com/blog/test-post</link>
        <pubDate>Mon, 22 Sep 2025 04:04:40 GMT</pubDate>
        <description><![CDATA[<p>test 222</p>]]></description>
      </item>
    
      <item>
        <title>How to set up Dokploy on Digital Ocean and deploy an application (with docker-compose)</title>
        <link>https://devmystify.com/blog/how-to-set-up-dokploy-on-digital-ocean-and-deploy-an-application-with-docker-compose</link>
        <pubDate>Wed, 17 Sep 2025 08:58:11 GMT</pubDate>
        <description><![CDATA[<p>Note: This tutorial contains affiliate links.</p>
<p><strong>PaaS</strong> (Platform as a Service) solutions are everywhere these days, but one that really grabbed my attention recently is 
    <a href="https://dokploy.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      **Dokploy**
    </a>
  . It has a Vercel-like user interface but is self-hosted, which is exactly what I was looking for. </p>
<p>Coming from 
    <a href="https://dokku.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Dokku
    </a>
  , having a GUI to manage my apps feels like a huge improvement (I don&#39;t hate the CLI, but a GUI is just easier). I liked it so much I decided to switch this very website, Devmystify, from Dokku to Dokploy.</p>
<p>This guide will walk you through setting up Dokploy and deploying an application using Docker Compose.</p>
<p>Before we jump in, I&#39;ll assume you already have:</p>
<ul>
<li>Your own <strong>Droplet:</strong> a DigitalOcean VPS works great (that’s what I personally use, 
    <a href="https://m.do.co/c/2df54168f4e4"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      you can get one here with $200 in credit
    </a>
  ), but a VPS from another provider will be fine as well.</li>
<li>A <strong>domain name:</strong> I’ve been using Namecheap for all my domain name needs for years (
    <a href="http://namecheap.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      click here to get one
    </a>
  ).</li>
</ul>
<p>Now, let’s get started!</p>
<hr>

    <h2 id="step-1-install-dokploy-on-your-droplet" class="scroll-mt-24">Step 1: Install Dokploy on Your Droplet</h2>
  <p>Installing Dokploy on your VPS is incredibly simple. First, make sure you are logged in as the <strong>root user</strong> or have <code>sudo</code>privileges. Then, you can either SSH into your Droplet or use the online terminal from DigitalOcean and run this single command:</p>
<pre><code class="language-bash">curl -sSL &lt;https://dokploy.com/install.sh&gt; | sh
</code></pre><p>After the command finishes, the installation will prompt you with the link to your Dokploy dashboard:</p>
<pre><code class="language-bash">Congratulations, Dokploy is installed!
Wait 15 seconds for the server to start
Please go to http://&lt;your-ip-from-your-vps&gt;:3000
</code></pre><p>And just like that, you&#39;ve successfully installed Dokploy on your Droplet! Pretty easy, right?</p>
<hr>

    <h2 id="step-2-initial-dokploy-configuration" class="scroll-mt-24">Step 2: Initial Dokploy Configuration</h2>
  <p>Navigate to the link from Step 1, and you&#39;ll be taken to the initial setup page. Simply fill out the form to create your Dokploy owner account. Once your account is ready, log in to your new dashboard.</p>
<p>
    <figure class="my-8">
      <div class="relative w-full max-w-3xl mx-auto rounded-xl overflow-hidden">
        <img
          src="https://azkydev-prod.s3.us-east-1.amazonaws.com/uploads/media/164/original-dashboard.png"
          alt="dashboard.png"
          class="w-full h-auto object-contain"
        />
      </div>
    </figure>
  </p>
<p>The UI looks really clean. Now, we need to set up a domain and HTTPS for our Dokploy dashboard.</p>
<p>Before you proceed, make sure you&#39;ve created an <strong>A record</strong> in your domain&#39;s DNS settings that points to your Droplet&#39;s IP address. For example, if your domain is <code>example.com</code>, you&#39;ll want to point <code>dokploy.example.com</code> to the Droplet&#39;s IP.</p>
<p>Go to the <strong>Web Server</strong> menu on the left side, and you will see the following form👇:</p>
<p>
    <figure class="my-8">
      <div class="relative w-full max-w-3xl mx-auto rounded-xl overflow-hidden">
        <img
          src="https://azkydev-prod.s3.us-east-1.amazonaws.com/uploads/media/165/original-dokploy-web-server.png"
          alt="dokploy-web-server.png"
          class="w-full h-auto object-contain"
        />
      </div>
    </figure>
  </p>
<p>Fill out the form with your domain name and select the <strong>HTTPS with Let&#39;s Encrypt</strong> option. Click <strong>Save</strong>. Dokploy will handle the rest, but note that it can take a few minutes for the DNS to propagate and the SSL certificate to be issued.</p>
<p>With that done, you now have a secure link to your dashboard.</p>
<hr>

    <h2 id="step-3-deploying-the-app" class="scroll-mt-24">Step 3: Deploying the app</h2>
  <p>Now for the fun part! Dokploy offers several ways to deploy services. Let&#39;s quickly go over each option:</p>
<ul>
<li><strong>Applications</strong>: You can use your repository, select a branch and a build path, and then deploy your application.</li>
<li><strong>Databases</strong>: Deploy a single database (Postgres, MySQL, MariaDB, MongoDB, and Redis) of your choice.</li>
<li><strong>Compose</strong>: This is similar to Applications, but you select a <code>docker-compose.yml</code> file instead of a build path.</li>
<li><strong>Template</strong>: Choose and deploy an open-source application from Dokploy&#39;s templates, such as n8n or Supabase.</li>
</ul>
<p>Before we get to the deployment, it&#39;s important to understand how Dokploy handles networking. Imagine Dokploy is a big office building. When you deploy each service—like your application or a database—separately through the UI, it&#39;s like putting each one on its own private floor. Each floor has its own secure network system, so an app on one floor can&#39;t connect to a database on a different floor.</p>
<p>This is why we can&#39;t connect a separately deployed database to our app using a simple internal connection string. But if we use a <code>docker-compose.yml</code> file, it&#39;s like we&#39;re using a single blueprint to place both services on the same floor. The <code>external: true</code> setting is the key that lets your new services use the existing building network, so they can talk to each other and with Dokploy&#39;s core services, all within the same secure environment.</p>
<p>
    <figure class="my-8">
      <div class="relative w-full max-w-3xl mx-auto rounded-xl overflow-hidden">
        <img
          src="https://azkydev-prod.s3.us-east-1.amazonaws.com/uploads/media/166/original-architecture.png"
          alt="architecture.png"
          class="w-full h-auto object-contain"
        />
      </div>
    </figure>
  </p>
<p><em>Thanks to the 
    <a href="https://docs.dokploy.com/docs/core/architecture"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Dokploy
    </a>
   team for this clear visual explanation!</em></p>
<p>Here&#39;s the <code>docker-compose.yml</code> file we&#39;ll use for both our app and database:</p>
<pre><code>services:
  db:
    image: postgres:15
    restart: always
    environment:
      POSTGRES_USER: tutorial-user
      POSTGRES_PASSWORD: tutorial-password
      POSTGRES_DB: tutorial-db
    volumes:
      - tutorial-db:/var/lib/postgresql/data
    networks:
      - dokploy-network

  tutorial-app:
    build:
      context: .
      dockerfile: Dockerfile
    env_file:
      - ./.env
    restart: always
    depends_on:
      - db
    networks:
      - dokploy-network

volumes:
  tutorial-db:

networks:
  dokploy-network:
    external: true
</code></pre><p>With this setup, your application can connect to the database using the internal connection string:</p>
<pre><code class="language-bash">postgresql://tutorial-user:tutorial-password@db:5432/tutorial-db
</code></pre><p>With everything prepared, the rest is easy. Simply select your linked <strong>GitHub</strong> account, choose the repository and branch, and specify the path to your <code>docker-compose.yml</code> file. Then, click <strong>Save</strong> and <strong>Deploy 🚀</strong>.</p>
<p>
    <figure class="my-8">
      <div class="relative w-full max-w-3xl mx-auto rounded-xl overflow-hidden">
        <img
          src="https://azkydev-prod.s3.us-east-1.amazonaws.com/uploads/media/167/original-dokploy-deploy-dashboard.png"
          alt="dokploy-deploy-dashboard.png"
          class="w-full h-auto object-contain"
        />
      </div>
    </figure>
  </p>
<hr>

    <h2 id="step-4-exposing-the-app" class="scroll-mt-24">Step 4: Exposing the app</h2>
  <p>Now that your application is up and running, there&#39;s just one step left: making it accessible to the world.</p>
<p>Navigate to the <strong>Domains</strong> tab. Select the service you want to link to a domain (in our case, the service name is <code>tutorial-app</code>). If you don&#39;t have any special configurations, just fill out the form: enter your domain name in the <strong>Host</strong> field, activate <strong>HTTPS</strong> with Let&#39;s Encrypt, and click <strong>Save</strong>.</p>
<p>And just like that, you have a live application!</p>
<p>
    <figure class="my-8">
      <div class="relative w-full max-w-3xl mx-auto rounded-xl overflow-hidden">
        <img
          src="https://azkydev-prod.s3.us-east-1.amazonaws.com/uploads/media/168/original-dokploy-app-domain.png"
          alt="dokploy-app-domain.png"
          class="w-full h-auto object-contain"
        />
      </div>
    </figure>
  </p>
<hr>

    <h2 id="wrap-up" class="scroll-mt-24">Wrap up</h2>
  <p>This wasn&#39;t a long tutorial because Dokploy makes deploying applications incredibly simple. You get a clean, modern GUI, easy-to-access logs, automatic redeployment on every code push (CI/CD), and many more features out of the box. And since it&#39;s self-hosted, it&#39;s completely free!</p>
<p>If you want to support the Dokploy team, they also offer a managed service called 
    <a href="https://dokploy.com/#pricing"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Dokploy Cloud
    </a>
  . It&#39;s worth checking out if you&#39;re interested. I hope this tutorial demonstrates how easy it is to host your application with Dokploy. If you have any suggestions or questions, feel free to leave a comment below!</p>]]></description>
      </item>
    
      <item>
        <title>ES2025: New & Shiny JavaScript Features</title>
        <link>https://devmystify.com/blog/es2025-new-shiny-javascript-features</link>
        <pubDate>Wed, 10 Sep 2025 04:09:35 GMT</pubDate>
        <description><![CDATA[<p>JavaScript, one of the most used programming languages in the world, keeps evolving to make itself more efficient and expressive, but more importantly, to make our lives as developers easier. </p>
<p>This year, it officially received a new set of features: ES2025.</p>
<p>Here are 8 of them, with examples, so you can discover what’s new and maybe bring them into your projects (or not… but hey, it’s always fun to learn something new &amp; shiny).</p>
<hr>

    <h2 id="1-promisetry" class="scroll-mt-24">#1. Promise.try()</h2>
  <p><code>Promise.try()</code> is a great new way to deal with inconsistent functions, sometimes found in older or legacy code that you don’t necessarily have control over. By inconsistent, I mean that they sometimes return a value immediately (synchronously), and other times, they return a promise (asynchronously). </p>
<p>This problem forces you to write messy and defensive code like this:</p>
<pre><code class="language-jsx">// Imagine a messy, inconsistent function you have no control over
function getProfile(userId) {
	if (userId === &#39;sync-error&#39;) {
		throw new Error(&#39;No userId provided.&#39;)
	}

	if (userId === &#39;sync-user&#39;) {
		return {name: &#39;John&#39;, id: userId },
	}

	return new Promise((resolve, reject) =&gt; {
		setTimeout(() =&gt; {
			if (userId === &#39;async-error&#39;) {
			reject(new Error(&#39;Server failed to fetch user data&#39;));
			} else {
			resolve({ name: &#39;Jane&#39;, id: userId });
			}
		}, 100);
	});
}

// Here&#39;s how you might deal with that function
try {
	const profile = getProfile(&#39;sync-user&#39;);
	if (profile instanceof Promise) {
		profile.then(data =&gt; {
	      console.log(&#39;Async success:&#39;, data);
	    }).catch(error =&gt; {
	      console.error(&#39;Async error:&#39;, error.message);
	    });
	} else {
		console.log(&#39;Sync success:&#39;, profile);
	}
} catch (error) {
	console.error(&#39;Sync error:&#39;, error.message);
}
</code></pre><p>That’s kind of confusing and hard to maintain. With the new <code>Promise.try()</code>, you can refactor it to this:</p>
<pre><code class="language-jsx">// That new helper can handle all the errors the messy 
// function could throw at you.
Promise.try(() =&gt; getProfile(&#39;async-error&#39;))
  .then(profile =&gt; console.log(&#39;Success:&#39;, profile))
  .catch(error =&gt; console.error(&#39;Error with async-error:&#39;, error.message));
</code></pre><p>With this new feature, you now have one consistent way to handle a function that could either be synchronous or asynchronous, while allowing for the code to be way more readable and maintainable.</p>
<hr>

    <h2 id="2-set-methods" class="scroll-mt-24">#2. `Set` Methods</h2>
  <p>Before these new <code>Set</code> methods were introduced, performing set operations in JavaScript was quite tedious, very manual, and overly verbose.</p>
<p>Take a look at this example:</p>
<pre><code class="language-jsx">const cookingClub = new Set([&#39;Adam&#39;, &#39;Eve&#39;, &#39;Reggie&#39;, &#39;Jane&#39;]);
const volunteers = new Set([&#39;Reggie&#39;, &#39;Jane&#39;, &#39;John&#39;, &#39;Bob&#39;]);

// Now we want to find out who is in both lists
const commonMembers = [];
cookingClub.forEach(member =&gt; {
	if (volunteers.has(member)) {
		commonMembers.push(member);
	}
});

console.log(commonMembers); // [&#39;Reggie&#39;, &#39;Jane&#39;]
</code></pre><p>It works — but that’s a lot of code for something so simple.</p>
<p>With the new methods, it becomes much cleaner and more semantic:</p>
<pre><code class="language-jsx">const commonMembers = cookingClub.intersection(volunteers);
console.log(commonMembers); // Set(2) { &#39;Reggie&#39;, &#39;Jane&#39; }

// or we can find who is in the cooking club but not a volunteer
const differenceMembers = cookingClub.difference(volunteers);
console.log(differenceMembers) // Set(2) { &#39;Adam&#39;, &#39;Eve&#39; }
</code></pre><p>No more manual loops. Just one-liners that read exactly like what you’re trying to do.</p>
<p>Other new methods, like <code>union</code> and <code>symmetricDifference</code>, will give you even more power. If the terms bring back memories of math class, don’t worry — the 
    <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      MDN docs
    </a>
   have a great visual guide that makes them click.</p>
<p>If you ever need to compare sets or arrays, these methods will make your code shorter, clearer, and a lot easier to maintain.</p>
<hr>

    <h2 id="3-iterator-helpers" class="scroll-mt-24">#3. Iterator Helpers</h2>
  <p>Alright, I’m sure you’re familiar with iterators in JavaScript, but if you’re not, here’s a quick reminder: an iterator is simply an object that has a <code>next()</code> method, which returns a value in the form of <code>{ value: foo, done: false }</code> . </p>
<p>Now, what about generators? To keep it simple, a generator is a function defined with an asterisk after the <code>function</code> keyword (<code>function*</code>) that uses the <code>yield</code> keyword to return values, one at a time:</p>
<pre><code class="language-jsx">function* numberGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = numberGenerator();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
</code></pre><p>Now that we’re on the same page, what exactly are “iterator helpers”?</p>
<p>They’re basically the good old array methods we know and love (<code>.map()</code>, <code>.filter()</code>, <code>.reduce()</code>, etc.), but available directly on iterators.</p>
<p>One practical use case I can think of is reading a huge CSV file.</p>
<p>Here’s how you may do it, the old fashioned way:</p>
<pre><code class="language-jsx">// A simple CSV reader
function* csvReader(csvString) {
  const lines = csvString.split(&#39;\\n&#39;);
  for (const line of lines) {
    if (line) yield line.split(&#39;,&#39;);
  }
}

// Simulate millions of lines
const csvData = `id,product,category,price\\n1,apple,fruit,1.00\\n
2,banana,fruit,0.50\\n
3,tomato,vegetable,0.75\\n...millions of lines...`;

// Loads the entire dataset into memory - not a good 
// idea, but this is for science!
const allRecords = [...csvReader(csvData)];

let totalPrice = 0;
for (const record of allRecords) {
  const [id, product, category, price] = record;
  if (category === &#39;fruit&#39;) {
    totalPrice += parseFloat(price);
  }
}

console.log(&#39;Total fruit price:&#39;, totalPrice);
</code></pre><p>Sure, this is a practice example, but scale it up to millions of lines and you’ll quickly run into trouble. Not only do we run into memory issues, but the readability isn’t great either. The new ES2025 iterator helpers give us a much nicer way to solve this:</p>
<pre><code class="language-jsx">// A simple CSV reader
function* csvReader(csvString) {
  const lines = csvString.split(&#39;\\n&#39;);
  for (const line of lines) {
    if (line) yield line.split(&#39;,&#39;);
  }
}

// Simulate millions of lines
const csvData = `id,product,category,price\\n1,apple,fruit,1.00\\n
2,banana,fruit,0.50\\n
3,tomato,vegetable,0.75\\n...millions of lines...`;

const records = csvReader(csvData);

// Use a chainable, lazy pipeline to process the data
const fruitPrices = records
  .drop(1) // Drop the header row
  .filter(record =&gt; record[2] === &#39;fruit&#39;) // Filter for &#39;fruit&#39; category
  .map(record =&gt; parseFloat(record[3]));   // Convert price to number

// Consume the iterator to calculate the total
let totalPrice = 0;
for (const price of fruitPrices) {
  totalPrice += price;
}

console.log(&#39;Total fruit price:&#39;, totalPrice);
</code></pre>
    <h3 id="why-this-is-better" class="scroll-mt-24">Why this is better</h3>
  <ul>
<li>We don’t put all records into a single array; instead, we keep them as an iterator. That means nothing is processed ahead of time—<code>map</code> and <code>filter</code> only run on one record at a time as we iterate.</li>
<li>From a readability standpoint, this approach is much clearer: it shows the step-by-step process in a declarative style.</li>
</ul>
<p>I won’t go through 
    <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      every helper method
    </a>
   here — there are a lot — but hopefully this gives you a good idea of how to use them in a real-world scenario.</p>
<hr>

    <h2 id="4-float16array" class="scroll-mt-24">#4. Float16Array</h2>
  <p>To keep it short: it’s a new typed array that stores 16-bit floating-point numbers. That’s it.</p>
<p>You can almost skip to the next part, because that’s really the whole story. Before this, JavaScript only had <code>Float32Array</code>, and now it has a smaller version of floating-point numbers.</p>
<p>I can’t imagine myself using this feature often, but if you’re working with low-level coding, machine learning, or you want to squeeze every bit of performance out of your code, this new typed array might serve you well.</p>
<p>In summary: the use case is memory efficiency—using the smallest data type for the right job.</p>
<p>But I can do you one better by visualizing it:</p>
<pre><code class="language-jsx">// Create a Float32Array to store 100,000 numbers
const float32Array = new Float32Array(100000);

// Each number takes 4 bytes (32 bits), so the total size is:
const float32Size = float32Array.byteLength;
console.log(`Float32Array size: ${float32Size} bytes`); // Output: 400000 bytes

// Time for the new type!
// Create a Float16Array to store the same number of elements
const float16Array = new Float16Array(100000);

// Each number takes 2 bytes (16 bits), so the total size is:
const float16Size = float16Array.byteLength;
console.log(`Float16Array size: ${float16Size} bytes`); // Output: 200000 bytes
</code></pre><p>So yeah, same number of elements, but half the memory. </p>
<hr>

    <h2 id="5-import-attributes-json-modules" class="scroll-mt-24">#5. Import Attributes & JSON Modules</h2>
  <p>These two features are related, so I think we can bundle them together.</p>
<p>You might have come across syntax like this in a TypeScript file:</p>
<pre><code class="language-tsx">import data from &quot;./data.json&quot; assert { type: &quot;json&quot; };
</code></pre><p>The <code>assert</code> syntax was intended to provide hints to the runtime, telling it to treat the file as JSON data rather than executable code.</p>
<p>But in ES2025, this syntax is deprecated in favor of a new feature: the <code>with</code> keyword.</p>
<pre><code class="language-tsx">import data from &quot;./data.json&quot; with { type: &quot;json&quot; };
</code></pre><p>It does the same thing, but <code>with</code> is now the standard.</p>

    <h3 id="use-case" class="scroll-mt-24">Use case:</h3>
  <blockquote>
<p>The primary use case is to load non-JS modules, such as JSON modules and CSS modules.</p>
<ul>
<li>
    <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import/with#description"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      *MDN Web Docs*
    </a>
  </li>
</ul>
</blockquote>
<p>I think this feature offers a lot: better clarity in the code and improved security, since you explicitly tell the runtime what type of file you’re importing instead of letting it assume anything.</p>

    <h3 id="hands-on-example" class="scroll-mt-24">Hands-On Example</h3>
  <pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Playground&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div class=&quot;playground&quot;&gt;
    &lt;/div&gt;
    &lt;script type=&quot;module&quot; src=&quot;app.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><pre><code class="language-css">.playground {
  padding: 1rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  background-color: red;
}
</code></pre><pre><code class="language-json">{
  &quot;name&quot;: &quot;Playground&quot;
}
</code></pre><pre><code class="language-jsx">import styles from &#39;./foo-style.css&#39; with { type: &#39;css&#39; };
import data from &#39;./data.json&#39; with { type: &#39;json&#39; };

document.adoptedStyleSheets.push(styles);
document.querySelector(&#39;.playground&#39;).textContent = data.name;
</code></pre><p>So we have 4 files:</p>
<ul>
<li><strong>index.html</strong> → displays the result</li>
<li><strong>CSS and JSON files</strong> → test the new import features</li>
<li><strong>JavaScript file</strong> → the heart of this simple web app</li>
</ul>
<p>And this is the result:</p>
<p>
    <figure class="my-8">
      <div class="relative w-full max-w-3xl mx-auto rounded-xl overflow-hidden">
        <img
          src="https://res.cloudinary.com/dhyyl3snm/image/upload/v1757477222/es2025-import-result_ahw0ie.png"
          alt="result"
          class="w-full h-auto object-contain"
        />
      </div>
    </figure>
  </p>
<p>This might not be a huge change, but it does make imports clearer and helps standardize the process.</p>
<p>⚠️ Disclaimer: The import attribute code above works in Chrome, but I’m not sure how many browsers support it yet. Safari, for example, doesn’t support it at the time of writing.</p>
<hr>

    <h2 id="6-regexpescape" class="scroll-mt-24">#6. RegExp.escape()</h2>
  <p>When users type search terms, they almost always expect a <strong>literal match.</strong></p>
<p>For example, if a user enters:</p>
<pre><code class="language-jsx">cat*
</code></pre><p>Most users mean <strong>the full word</strong> <code>cat*</code>, not the regex version, i.e. “cat followed by 0 or more <code>t</code>&quot;.</p>
<p>The same goes for something like:</p>
<pre><code class="language-jsx">cat.
</code></pre><p>A user expects to find <code>&quot;cat.&quot;</code>, not “cat followed by any single character”.</p>
<p>So what’s the problem?</p>
<p>In regular expressions, many characters have <strong>special meaning</strong> instead of being a normal character.</p>
<p>Mentioned examples:</p>
<ul>
<li><code>*</code> → repeat he previous character 0 or more times</li>
<li><code>.</code> → match any single character (except newline)</li>
</ul>
<p>So if we directly use the user’s input without escaping, we end up with:</p>
<pre><code class="language-jsx">const text = &#39;cat, cot, cut, cat* cat.&#39;;

// Example 1: user types &#39;cat*&#39;
const regex1 = new RegExp(&#39;cat*&#39;, &#39;g&#39;); // add &#39;g&#39; flag so we see all matches
console.log(text.match(regex1)); 
// 👉 [&#39;cat&#39;, &#39;cat&#39;, &#39;cat&#39;]
// ✅ Matches: &#39;cat&#39;, &#39;cat*&#39;, &#39;cat.&#39;
// Note: the &#39;*&#39; applies to the preceding &#39;t&#39;, so the match is just &quot;cat&quot;.
// (* repeats &#39;t&#39;)

// Example 2: user types &#39;cat.&#39;
const regex2 = new RegExp(&#39;cat.&#39;, &#39;g&#39;);
console.log(text.match(regex2)); 
// 👉 [&#39;cat,&#39;, &#39;cat*&#39;, &#39;cat.&#39;]
// ✅ Matches: &#39;cat,&#39;, &#39;cat*&#39;, &#39;cat.&#39;
// (. matches any single character)
</code></pre><p>That left us with 2 options.</p>
<ol>
<li>Escaping manually</li>
</ol>
<pre><code class="language-jsx">// Escape regex special characters
const escapeRegex = (str) =&gt; str.replace(/[.*+?^${}()|[\]\\]/g, &#39;\\$&amp;&#39;);

const text = &#39;cat, cot, cut, cat* cat.&#39;;

// User types &#39;cat*&#39;
const escapedRegex1 = new RegExp(escapeRegex(&#39;cat*&#39;));
console.log(text.match(escapedRegex1)); 
// 👉 Matches only: &#39;cat*&#39;

// User types &#39;cat.&#39;
const escapedRegex2 = new RegExp(escapeRegex(&#39;cat.&#39;));
console.log(text.match(escapedRegex2)); 
// 👉 Matches only: &#39;cat.&#39;
</code></pre><p>It works, but it’s boilerplate and easy to get wrong.</p>
<ol start="2">
<li>The new ES2025 <code>RegExp.escape()</code></li>
</ol>
<pre><code class="language-jsx">const text = &#39;cat, cot, cut, cat* cat.&#39;;

// User types &#39;cat*&#39;
const safeRegex1 = new RegExp(RegExp.escape(&#39;cat*&#39;));
console.log(text.match(safeRegex1)); 
// 👉 Matches only: &#39;cat*&#39;

// User types &#39;cat.&#39;
const safeRegex2 = new RegExp(RegExp.escape(&#39;cat.&#39;));
console.log(text.match(safeRegex2)); 
// 👉 Matches only: &#39;cat.&#39;
</code></pre><p>No more writing your own escape logic — the engine does it for you.</p>
<p>I’d say this addition is a good one. It removes the need for manual escaping and ensures that user input is always treated as a literal string.</p>
<hr>

    <h2 id="7-regexp-pattern-modifiers-inline-flags" class="scroll-mt-24">#7.  RegExp Pattern Modifiers (Inline Flags)</h2>
  <p>Another RegExp QoL update from ES2025. This feature lets you apply a flag to a specific part of a regular expression. It’s really useful when you want different parts of your pattern to behave in different ways, instead of relying on a global modifier.</p>
<p>Let’s start with the syntax below:</p>
<ul>
<li><code>(?i:...)</code> → turn <strong>on</strong> case-insensitive matching <em>just for this group</em></li>
<li><code>(?-i:...)</code> → turn <strong>off</strong> case-insensitive matching <em>just for this group</em></li>
</ul>
<p>This also works for other flags like <code>i</code>, <code>m</code>, and <code>s</code>.</p>
<pre><code class="language-jsx">// (?i:...) - Activates case-insensitivity
const regex1 = /(?i:hello)/;
regex1.test(&quot;Hello&quot;); // true

// (?-i:...) - Deactivates case-insensitivity
const regex2 = /^(?-i:hello)$/i; // The global &#39;i&#39; is active, but is turned off for &#39;hello&#39;
regex2.test(&quot;hello&quot;); // true
regex2.test(&quot;HELLO&quot;); // false
</code></pre><blockquote>
<p>Quick note: Other modifiers are not supported, since they would either make your regex overly complicated or simply wouldn’t make sense when applied locally instead of globally.</p>
</blockquote>

    <h3 id="lets-understand-it-better-with-an-example" class="scroll-mt-24">Let’s understand it better with an example.</h3>
  <p>Imagine you need to match a file path that starts with a <strong>case-sensitive drive letter</strong> (<code>C:</code>), followed by a <strong>case-insensitive directory name</strong> (<code>Users</code>), and then a <strong>specific case-sensitive filename</strong> (<code>MyFile.txt</code>).</p>

    <h3 id="old-way" class="scroll-mt-24">Old way</h3>
  <pre><code class="language-jsx">// This regex makes the entire expression case-insensitive.
const regex = /C:\\Users\\MyFile\.txt/i;

console.log(regex.test(&quot;c:\\users\\myfile.txt&quot;)); 
// Output: true (too broad - it should fail!)
</code></pre><p>See the problem? This regex matches both <code>c:</code> and <code>C:</code>, which might not be the behavior we want. And just thinking about building a complicated regex to cover all these cases is enough to make me want to take a coffee break.</p>

    <h3 id="new-way" class="scroll-mt-24">New way</h3>
  <pre><code class="language-jsx">// The (?i:...) applies the case-insensitive flag only to &quot;users&quot;
const regex = /C:\\(?i:Users)\\MyFile\.txt/;

console.log(regex.test(&quot;C:\\users\\MyFile.txt&quot;)); // Output: true (correct)
console.log(regex.test(&quot;C:\\Users\\MyFile.txt&quot;)); // Output: true (correct)
console.log(regex.test(&quot;c:\\users\\MyFile.txt&quot;)); // Output: false (correct)
console.log(regex.test(&quot;C:\\users\\myfile.txt&quot;)); // Output: false (correct)
</code></pre><p>This addition is pretty elegant, as it allows you to write precise regex that match exactly what you want, without needing complicated workarounds.</p>
<hr>

    <h2 id="8-duplicate-named-capture-groups" class="scroll-mt-24">#8. Duplicate Named Capture Groups</h2>
  <p>Traditionally, every named capture group in a JavaScript regex had to be unique. If you tried to reuse a name, it would throw a <code>SyntaxError</code>. This often led to repetitive names like <code>year1</code> or <code>year2</code>, even when they represented the same concept.</p>
<p>Well, that restriction has been lifted: you can now reuse the same name in a single regex, and the results will be returned as an array of matches under that name.</p>
<pre><code class="language-jsx">// New ES2025 behavior
const regex = /(?&lt;num&gt;\d+)-(?&lt;num&gt;\d+)-(?&lt;num&gt;\d+)/;
const result = regex.exec(&quot;123-456-789&quot;);

console.log(result.groups.num);
// Output: [&quot;123&quot;, &quot;456&quot;, &quot;789&quot;]
</code></pre>
    <h3 id="example" class="scroll-mt-24">Example:</h3>
  
    <h3 id="old-way" class="scroll-mt-24">Old way</h3>
  <pre><code class="language-jsx">const str = &quot;Today is 2025-09 and tomorrow is 09-2025&quot;;

// Had to use two different names: year1 and year2
const regex = /(?&lt;year1&gt;[0-9]{4})-[0-9]{2}|[0-9]{2}-(?&lt;year2&gt;[0-9]{4})/g;

const result = [...str.matchAll(regex)];

// Collect whichever group matched
const years = result.map(r =&gt; r.groups.year1 || r.groups.year2);

console.log(years);
// Output: [&quot;2025&quot;, &quot;2025&quot;]
</code></pre>
    <h3 id="new-way" class="scroll-mt-24">New way</h3>
  <pre><code class="language-jsx">const str = &quot;Today is 2025-09 and tomorrow is 09-2025&quot;;

// Same group name reused across both alternations
const regex = /(?&lt;year&gt;[0-9]{4})-[0-9]{2}|[0-9]{2}-(?&lt;year&gt;[0-9]{4})/g;

const result = [...str.matchAll(regex)];

console.log(result.map(r =&gt; r.groups.year));
// Output: [&quot;2025&quot;, &quot;2025&quot;]
</code></pre><p>This is much cleaner, with no need for names like <code>year1</code> and <code>year2</code></p>
<p><strong>Note</strong>: This feature is still new and not yet widely available in all JavaScript environments, so please be cautious when using it in production code.</p>

    <h1 id="wrapping-up" class="scroll-mt-24">Wrapping Up</h1>
  <p>And that’s a tour of the shiny new features in <strong>ES2025!</strong></p>
<p>Some of these updates are small quality-of-life helpers — like <code>RegExp.escape()</code> and inline pattern modifiers — while others open up brand-new possibilities, like iterator helpers or <code>Float16Array</code> for squeezing more performance out of your code.</p>
<p>The important thing isn’t memorizing every single detail, but knowing what tools exist so you can reach for them when the time is right. Even if you don’t need <code>Promise.try()</code> or duplicate capture groups today, just being aware they’re available can save you hours of work down the road.</p>
<p>JavaScript continues to evolve in ways that make it more expressive, more efficient, and frankly more enjoyable to write. Try out these features in your own projects, keep an eye on browser and runtime support, and have fun exploring what’s new.</p>
<p>Because at the end of the day: better tools mean less boilerplate, cleaner code, and more time to build the stuff you actually care about.</p>
<hr>]]></description>
      </item>
    
      <item>
        <title>The (re)birth of Devmystify</title>
        <link>https://devmystify.com/blog/the-re-birth-of-devmystify</link>
        <pubDate>Fri, 29 Aug 2025 09:02:43 GMT</pubDate>
        <description><![CDATA[<p>More than a decade ago, I started the earliest version of this site, as nothing more than a scrappy personal blog. It changed names a few times, but the spirit stayed the same: share what I learn, as I learn it. Over the years that became tutorials and books—mostly around Ruby on Rails—aimed at helping other developers level up.</p>
<p>Then life happened. Work got intense, the site stayed quiet, and a lot changed. Here’s what I’ve been up to, what’s coming back, and where Devmystify is headed next.</p>

    <h2 id="what-ive-been-up-to-in-the-past-decade" class="scroll-mt-24">What I’ve been up to in the past decade</h2>
  
    <h3 id="2016-2017" class="scroll-mt-24">2016-2017</h3>
  <p>I lived the digital-nomad life—traveling around SEA and Europe as I tackled Upwork gigs. It taught me how to sell my skills, manage clients, and ship value quickly. I reached Top Rated and made a few essential connections.</p>

    <h3 id="2017-2020" class="scroll-mt-24">2017-2020</h3>
  <p>Something funny happened during the summer of 2017: one of my friends posted something about Ethereum. I had just begun getting back into crypto and the blockchain world, so it piqued my interest and I got in touch with him. That friend was the CTO of Omise, a payment processing company in Thailand, and they were creating OmiseGO, a new company focused on blockchain and its uses in the payment world.</p>
<p>A couple of weeks later, I was officially hired and joined as a Team Lead. With the blockchain bubble in full swing, this was too good of an opportunity to pass up, and I don’t regret it at all!</p>
<p>Over the following years, I built a team of great engineers. We created an open-source blockchain-connected eWallet using Elixir. Three wild years. It was exhilarating, exhausting, and it pulled my focus away from this site.</p>
<p>I was promoted to Director of Engineering, which was great, as our engineering team was around 25 people at the time. But good things don’t last.</p>
<p>The company was sold and nearly everyone was let go. </p>

    <h3 id="2020-2024" class="scroll-mt-24">2020 - 2024</h3>
  <p>I landed at a small web agency in Bangkok and hustled to bring in clients and projects to help the team succeed.</p>

    <h3 id="meanwhile-in-life" class="scroll-mt-24">Meanwhile in life</h3>
  <p>I got married and became a dad to a little boy. Busy is an understatement.</p>

    <h3 id="2024-2025" class="scroll-mt-24">2024 - 2025</h3>
  <p>After that, I took what I’d learned and started my own company with my partner, working with clients in the U.S. from Phuket. We’re growing carefully, but also <strong>diversifying</strong>. Consulting pays the bills, but I want to build <strong>lasting value</strong>: books and courses that distill 15 years of software engineering and technical leadership—and finally some SaaS products I’ve been itching to launch. I’m also leaning into content creation, especially <strong>YouTube</strong>, to demystify how developers can learn better and earn more.</p>

    <h2 id="why-freelancing-still-matters" class="scroll-mt-24">Why Freelancing Still Matters</h2>
  <p>Even as I diversify into products and teaching, freelancing is still at the core. Right now, selling software services is our company’s primary source of revenue. </p>
<p>Setting your own schedule and working from anywhere (hello from Phuket 👋) is a freedom many developers want. Sure, freelancing has its own challenges, but it turns you into a business. You set your hours, choose your clients, and earn what you’re worth. That creates room to reinvest into side projects and bigger ventures.</p>
<hr>

    <h2 id="about-the-books-modular-rails-master-ruby-web-apis" class="scroll-mt-24">About the Books: *Modular Rails* & *Master Ruby Web APIs*</h2>
  <p>Some of you may be wondering: <em>Where did the books go?</em> I made both <strong>free to read online</strong> for a long time, but I couldn’t keep them current—they were stuck around <strong>Rails 5</strong>. Outdated books help no one.</p>
<p>So I took them down… to <strong>rewrite and re-release</strong> them properly with my team. They’re coming back fully refreshed for modern Rails.</p>
<hr>

    <h2 id="rails-ai-and-the-future-of-developer-work" class="scroll-mt-24">Rails, AI, and the Future of Developer Work</h2>
  <p>Interesting twist: after years of mostly doing JavaScript, I’m now back in the trenches working on a <strong>15-year-old Rails monolith</strong>. It gave me some white hairs, but it also made me realize that Rails isn’t dead. Far from it, apparently. Maybe in a few decades, the Rails Cowboys will be a thing (see 
    <a href="https://cobolcowboys.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Cobol Cowboys
    </a>
  , a team who maintain COBOL applications, a language created in 1959).</p>
<p>That said, Devmystify won’t be Rails-only. <strong>React</strong> is everywhere, and I use a lot of <strong>Next.js</strong> these days (heck, this website was written with Next.js!). </p>
<p>And of course, I have to mention AI, as it is changing how we work. My focus is practical: how to use AI to ship faster, communicate better, and be more productive, without chasing every shiny demo. </p>
<p>No vibe coding allowed though.</p>
<hr>

    <h2 id="what-im-working-on-now-and-next" class="scroll-mt-24">What I’m working on now (and Next)</h2>
  <ul>
<li>📚 <strong>Book relaunches</strong> – <em>Modular Rails</em> and <em>Master Ruby Web APIs</em>, fully updated (Rails <strong>7 &amp; 8</strong>).</li>
<li>🎓 <strong>A complete course on freelancing</strong></li>
<li>🎥 <strong>The Devmystify YouTube channel</strong> – real talk about developer income streams: freelancing, consulting, products, content, and how to pick what fits <em>you</em>.</li>
<li>🛠️ <strong>SaaS projects</strong> – long-standing ideas I can now staff and execute.</li>
<li>🌱 <strong>And plenty more as the community grows.</strong></li>
</ul>
<hr>

    <h2 id="what-devmystify-stands-for" class="scroll-mt-24">What Devmystify Stands For</h2>
  <p>Devmystify’s mission is simple: help developers learn smarter and earn more, by demystifying the roadblocks holding you back. That means practical tutorials, honest career advice, and real business case studies. If you’re a developer who wants more control over your time, income, and career direction, you’re in the right place.</p>
<p>If this resonates with you, 
    <a href="https://devmystify.com/join"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      you should join the newsletter
    </a>
   — I’ll share updates, practical dev tips, and behind-the-scenes lessons you won’t find anywhere else.</p>
<p>Finally, if you’ve been here since the early blog days, thank you. If you’re new, welcome. I’m excited to ship useful things again and to build this next chapter with you. </p>]]></description>
      </item>
    
      <item>
        <title>Modularity in Elixir</title>
        <link>https://devmystify.com/blog/modularity-in-elixir</link>
        <pubDate>Sun, 08 Jun 2025 16:02:34 GMT</pubDate>
        <description><![CDATA[<p>I&#39;m a big fan of modularity with Ruby on Rails. If you don&#39;t know, Modularity is basically the separation of concerns principle applied at the level of your entire application.</p>
<p>Modular in Rails can be implemented using a feature that came with Rails 3: Engines. From the 
    <a href="https://guides.rubyonrails.org/v3.2.13/engines.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Ruby on Rails guides
    </a>
  :</p>
<blockquote>
<p>Engines can be considered miniature applications that provide functionalities to their host applications.</p>
</blockquote>
<p>It&#39;s unfortunate that this feature, making the whole modular approach possible, is only present at the framework level, and not in the language itself.</p>

    <h2 id="elixir" class="scroll-mt-24">Elixir</h2>
  <p>About 2 years ago, I started playing around with Elixir. I built a project with it for fun, but barely scratched the surface of its powers. A few months later, I started a new job as Lead Engineer and started building a more advanced Elixir application.</p>
<p>Through research, test implementation and help from my first team member (who had a background in Erlang), I was able to wrap my head around a lot of new concepts: functional programming, immutability, process management, GenServers, etc. But the thing I got most excited about was the 
    <a href="https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-projects.html#umbrella-projects"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Umbrella feature
    </a>
  .</p>
<p>It&#39;s basically everything that was hacked together to make Rails engines work, but cleanly implemented in the language itself and offered with proper dependencies management. You can adjust how you want your sub-apps (as we call the individual applications in Umbrella apps) to interact and define if you want them to actually start and manage their own processes.</p>
<p>It was love at first sight.</p>
<p>I decided right away that we should build the project as an Umbrella app, in order to properly structure all the moving parts. It was so easy to get everything working that I&#39;m not sure I want to go through the pain of doing it in Ruby anymore.</p>
<p><strong>You can look at the sub-applications inside an Umbrella app as internal micro-services, each dealing with its own set of responsibilities and working with the others to provide an actually useful set of features to your users.</strong></p>

    <h2 id="an-example" class="scroll-mt-24">An example</h2>
  <p>Now, let&#39;s see how it looks like in practice, by making a very simple Umbrella app. We won&#39;t go into the details, I&#39;m just trying to share the whole idea here, not how to actually code it.</p>
<p>We&#39;re going to be creating an example structure for a CRM application, that will offer the following features:</p>
<ul>
<li>Users &amp; Authentication management</li>
<li>Contacts / Leads management</li>
<li>All interactions with the CRM should be available through a regular web interface, as well as through a web API.</li>
</ul>
<p>We can actually structure it in two different ways (and probably more, but I found these two to be the most interesting ones). First, let&#39;s generate the Umbrella app and we&#39;ll go through the two approaches:</p>
<pre><code>mix new crm_umbrella --umbrella
</code></pre><pre><code>* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating apps
* creating config
* creating config/config.exs

Your umbrella project was created successfully.
Inside your project, you will find an apps/ directory
where you can create and host many apps:

    cd crm_umbrella
    cd apps
    mix new my_app

Commands like &quot;mix compile&quot; and &quot;mix test&quot; when executed
in the umbrella project root will automatically run
for each application in the apps/ directory.
</code></pre><pre><code>cd crm_umbrella/apps
</code></pre>
    <h3 id="the-business-logic-modules-silos-approach" class="scroll-mt-24">The "Business Logic Modules" / "Silos" approach</h3>
  <p>With this approach, we split the business logic of the CRM into modules, where each one contains everything it needs to function. In short, we will build a set of silos that connects the end user to the data layer.</p>
<p>To make things simpler, we start off with a <code>core</code> module where we will initialize a shared Ecto <code>Repo</code>:</p>
<pre><code>mix new core
</code></pre><p>We can then create two more sub-applications, one for the users and one for the contacts:</p>
<pre><code>mix new users
</code></pre><pre><code>mix new contacts
</code></pre><p>We will have the entire stack in each one of them:</p>
<ul>
<li><code>User</code> / <code>Contact</code> Ecto schemas</li>
<li>The usual Phoenix stuff: router, controllers, views, and templates.</li>
<li>Any needed modules between the data layer and the web layer.</li>
</ul>
<p>The last thing we&#39;ll probably want to add is a dispatcher sub-application. Since we have more than one application that can handle web requests (both the <code>users</code> and <code>contacts</code> apps can), we want to have one sub-application that receives all web request and dispatch them to the right place:</p>
<pre><code>mix new dispatcher
</code></pre><p>In the end, the dependency tree for our sub-apps will look like this:</p>
<pre><code>    dispatcher
   |         |
 users     contacts
    \        /
       core
</code></pre><p>This approach is interesting if you&#39;re looking for something similar to usual micro-services, you can just add more sub-apps in the middle layer and have different teams working on each one of them.</p>
<p><strong>Note that I usually dislike micro-services architecture, and I&#39;m only referencing it as an example here. I don&#39;t recommend going that route. My simple rule of thumb to define if micro-services are needed is that if one developer is going to work on more than one micro-service, then you don&#39;t need micro-services.</strong></p>

    <h3 id="the-functional-layer-modules-approach" class="scroll-mt-24">The "Functional Layer Modules" approach</h3>
  <p>This approach focuses more on cutting your application horizontally, into a stack of layers.</p>
<p>First, we create a sub-application where we will put all our database schemas. This will represent the data layer to the rest of the sub-apps, making it easier to store and retrieve records.</p>
<pre><code>mix new db
</code></pre><p>On top, we create another sub-app to hold the modules that will contain the business logic. To keep this simple, I&#39;m only creating one, but nothing prevents you from having more:</p>
<pre><code>mix new crm
</code></pre><p>Finally, we create sub-apps that can handle HTTP request and will be accessible by anyone. We create one for our website and one for our API. For the website app, we can either build a frontend using Phoenix templating system, or we could have a JavaScript &quot;Single Page Application&quot; that connects to the API sub-app. That&#39;s actually how the Admin Panel we built works.</p>
<pre><code>mix new web
</code></pre><pre><code>mix new api --module API
</code></pre><p>We end up with an Umbrella application structured like this:</p>
<pre><code>web   API
 |     |
 \     /
   CRM
    |
   DB
</code></pre><p>I personally tend to prefer this approach lately, especially for smaller teams.</p>

    <h2 id="the-end" class="scroll-mt-24">The End</h2>
  <p>I originally worked with the &quot;Silos&quot; approach for my first modular application, simply because the company I was working for wanted to be able to take those siloed features (encapsulated in modules) and add them to other applications. For example, a booking module could be used in an app for restaurants, for doctors or for massages. Another good example is 
    <a href="https://github.com/plataformatec/devise"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Devise
    </a>
  , which encapsulates the whole Authentication stack inside a Rails engine.</p>
<p>For that company project, we wanted to build a flexible, all-in-one, and easy to deploy application. We weren&#39;t planning to reuse modules anywhere else or run the sub-apps as micro-services. That&#39;s why it made sense for us to go with the Layered approach.</p>
<p>There is no right or wrong, both of those approaches are valid and there are definitely more ways to structure your modular applications. It really comes down to your needs.</p>
<p>Let me know which approach you prefer (or are currently using) and why in the comments :)</p>]]></description>
      </item>
    
      <item>
        <title>REST in peace</title>
        <link>https://devmystify.com/blog/rest-in-peace</link>
        <pubDate>Sun, 08 Jun 2025 15:57:34 GMT</pubDate>
        <description><![CDATA[<p>I used to be kind of a REST fanatic. I thought it was the holy grail, the secret sauce defined years ago (before the Web was cool) that would allow people to write awesome web APIs.</p>
<p>Oh boy, what a disappointment.</p>

    <h2 id="the-usual-origin-story" class="scroll-mt-24">The usual origin story</h2>
  <p>I started like everyone else, learning that a properly built web API needed to offer nice looking resources (<code>/users</code>), handling the various HTTP verbs (<code>get</code>, <code>post</code>, etc.) which are semantically loaded with rules and behaviors defined in RFCs. I learned those approaches that by working on Ruby on Rails applications. All was well for a while, and I spent a few years building apps that way.</p>
<p>I never took the time to dig deeper into Roy Fielding’s dissertation or learn more about REST, I thought I was building RESTful APIs. I never actually wondered why people didn’t call them “REST APIs”, was there REST-half APIs or partially-REST ones? ¯_(ツ)_/¯</p>
<p>By that point, I had spent the past two years building web APIs in Ruby and had seen all kinds of APIs. I felt I knew the right way to build RESTful APIs. I also wanted to write another programming book, so I naturally thought I had to share my knowledge.</p>

    <h2 id="writing-master-ruby-web-apis" class="scroll-mt-24">Writing Master Ruby Web APIs</h2>
  <p>The first thing I did, before writing a single line of text or code for MRWA,  was research. I went through a lot of content, starting with 
    <a href="https://amzn.to/2Fv7d6I"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      RESTful Web APIs
    </a>
   as I thought it would give me a great understanding of REST and what is a RESTful Web API. Well, it left me disappointed and wanting for more. There was so many mentions of the “Semantic Gap” that I felt most of the book was written from a theoretical perspective, so barely anything I could use in practice, for the book or my apps.</p>
<p>The Semantic Gap is the difference in understanding of web applications by humans and by programmed clients. We, as humans, are able to open the homepage of a website, discover linked pages and navigate around. The applications we build aren’t that smart, unfortunately, we need to tell them what to do. That’s why human-readable documentation is necessary for web APIs today, because humans need to read through them and implement their apps in the right way.</p>
<p>The current approach offered by REST is to basically try to bridge the gap as much as possible, until it is closed. Unfortunately, I don’t think we’re getting there in the coming years.</p>
<p>This realization was like a cold shower.  I decided to focus more on building  HTTP APIs, and the first step was dedicated a whole module to review the basics of building stuff for the Web. That meant reviewing and putting in practice all the features that came with HTTP (if they made sense in the context of an API), without worrying too much about all the REST side of things.</p>
<p>In the second module of the book, I teach how to build a complex Ruby on Rails web API by going up to level 2 in the Richardson’s Maturity Model. I felt that was enough since that’s what most businesses are doing today, and I wanted to teach something practical.</p>
<p>
    <figure class="my-8">
      <div class="relative w-full max-w-3xl mx-auto rounded-xl overflow-hidden">
        <img
          src="https://martinfowler.com/articles/images/richardsonMaturityModel/overview.png"
          alt="Richardson Maturity Model"
          class="w-full h-auto object-contain"
        />
      </div>
    </figure>
  </p>
<p>Picture from this post: 
    <a href="https://martinfowler.com/articles/richardsonMaturityModel.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Richardson Maturity Model
    </a>
  </p>

    <h2 id="getting-frustrated" class="scroll-mt-24">Getting frustrated</h2>
  <p>Finally, the last module was supposed to focus on HATEOAS. I tried, I really did. But I realized it wasn’t really practical. Sure, I could use hypermedia links to notify a client that an endpoint had moved (as long as the client had a way of handling this safely, as in don’t follow redirections  forever or get stuck in a loop), but I still needed to hardcode how and where to interact with the API.</p>
<p>There were some interesting specifications though, like the 
    <a href="https://devmystify.com/r/master-ruby-web-apis/exploring-more-hypermedia-formats#uid4"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Hydra spec
    </a>
  . With it, if I was building a client to show data in the way the API was built, I could just build screens dynamically based on the returned data (unfortunately, I wouldn’t be able to customize each screen to offer a better UX to the users). While the idea sounds good in theory, it has little practical value for our needs today.</p>
<p>In the end, I feel building clean HTTP APIs  is all about embracing good ol’ software engineering concepts and keeping your users happy.</p>

    <h2 id="chilling-out" class="scroll-mt-24">Chilling out</h2>
  <p>These days, I’m not really worrying about building RESTful APIs anymore. I don’t really use the word anymore. At my latest job, we decided to not bind our APIs to HTTP, and went down the JSON-RPC road, with full custom error codes, etc. We wanted to stay “communication protocol”-agnostic to offer the same kind of responses through HTTP, Websockets, and whatever else fancies our minds.</p>
<p>In conclusion, most Web APIs out there are just HTTP APIs, and that’s good enough. You can call them RESTful, I don’t even get mad anymore, simply because I don’t care, I&#39;ll just check the docs to see how to use it. I’m more interested in providing value to people with interfaces they will like.</p>
<p>You will need to provide a human-readable documentation with your API, and it doesn’t matter if it’s RESTful, RESTish, RPC, or something else - and it’s all okay. Maybe one day, the gap will be bridged, but until then, we should worry about building things people want to use.</p>
<p>A good example to illustrate this point is 
    <a href="https://github.blog/2016-09-14-the-github-graphql-api/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      the switch Github made
    </a>
  , going from a RESTful API (with some pretty nice Hypermedia support) to using 
    <a href="https://graphql.org"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      GraphQL
    </a>
  , because following the REST principles was bloating their API and making it not flexible enough for its users.</p>
<p>I’m actually quite interested in using GraphQL and making clean RPC APIs lately, so I’ll probably cover those topics more in the future.</p>

    <h2 id="relevant-parts-from-master-ruby-web-apis" class="scroll-mt-24">Relevant parts from Master Ruby Web APIs</h2>
  <ul>
<li>
    <a href="https://devmystify.com/r/master-ruby-web-apis/rest"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      A REST Overview
    </a>
  </li>
<li>
    <a href="https://devmystify.com/r/master-ruby-web-apis/alexandria-and-the-rest-constraints"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Why the app built in MRWA isn’t RESTful
    </a>
  </li>
</ul>

    <h2 id="interesting-articles" class="scroll-mt-24">Interesting articles</h2>
  <ul>
<li>
    <a href="https://www.freecodecamp.org/news/rest-is-the-new-soap-97ff6c09896d/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      REST is the new SOAP
    </a>
  </li>
<li>
    <a href="https://phil.tech/api/2017/12/18/rest-confusion-explained/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      No, it’s not.
    </a>
  </li>
<li>
    <a href="https://www.freecodecamp.org/news/follow-up-to-rest-is-the-new-soap-the-origins-of-rest-21c59d243438/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Yes, it is.
    </a>
  </li>
</ul>

    <h2 id="articles-that-resulted-from-my-frustration-a-long-time-ago" class="scroll-mt-24">Articles that resulted from my frustration a long time ago</h2>
  <ul>
<li>
    <a href="https://devmystify.com/b/stop-calling-your-web-apis-restful-it-doesnt-mean-anything-anymore"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      The Web API War - Devblast
    </a>
  </li>
<li>
    <a href="https://devmystify.com/b/calling-your-web-api-restful-youre-doing-it-wrong"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      RESTful APIs? You’re doing it wrong.
    </a>
  </li>
</ul>]]></description>
      </item>
    
      <item>
        <title>A Developer’s Guide To Fighting Desk Injuries</title>
        <link>https://devmystify.com/blog/a-developer-guide-to-fighting-desk-injuries</link>
        <pubDate>Sun, 08 Jun 2025 15:52:34 GMT</pubDate>
        <description><![CDATA[<p>Working in a modern office brings its share of conveniences, but as anyone who sits in front of a desk all day long knows, it’s easy to slide into unhealthy habits. Humans weren’t really designed for sitting in front of desks, staring at screens all day... but that’s exactly what most of us do, 40 hours or more per week.</p>
<p>As developers, we need computers for most of our work, and that usually means sitting in front of one for hours on end, with every little physical activity.</p>

    <h2 id="youve-got-to-move-it-move-it" class="scroll-mt-24">You've got to move it, move it</h2>
  <p>One of the biggest problems of working from a desk is that you don’t get enough opportunities for passive exercise. Take the perennial college job of working in a grocery store as an example - lifting heavy boxes or bags, climbing ladders in the backroom, or just walking around all day - you’ve essentially got a workout built in. Once you’ve traded in your nametag and pulled up a seat in front of a screen, the passive health benefits start to drain away.</p>
<p>One tip that always gets tossed around is the 1/10 rule - for every hour, get up and walk around for 10 minutes. If you work in a bigger office, this one is a little easier to turn into a habit - walk to a coworker’s desk, go hangout near the coffee machine, or take a spin around the breakroom. If you work from home or in a smaller team, use your phone’s timer to set break reminders for yourself. Or if your working hours are flexible, run out to the post office, grocery store, or take a quick walk around the block every two hours or so. In any case, you’re reaping double the benefits from unplugging for just a few minutes - getting a little exercise will also improve your mental clarity, energy, and possibly your overall job performance.</p>

    <h2 id="ergonomics" class="scroll-mt-24">Ergonomics</h2>
  <p>“Ergonomics” was the buzzword in the office of the early 90s. As computer use became more and more frequent, so too did headaches, backaches, and repetitive strain injuries like 
    <a href="http://www.webmd.com/pain-management/carpal-tunnel/carpal-tunnel-syndrome-topic-overview"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      carpal tunnel syndrome
    </a>
   as the new occupational hazards. The solution? Proper design of things we used everyday, like keyboards, chairs, or computer mice.</p>
<p>Nowadays, there are tons of tools you can buy that claim to reduce repetitive strains with the added benefit of making your workspace a touch more futuristic. The split keyboard and chairs with increased lumbar support and cushioning are becoming more and more popular.</p>
<p>But an ergonomic workspace also means a lot of small, non-obvious adjustments. This might mean the positioning of your keyboard, the height of your computer screen, or the distance between your chair and the screen. No matter how expensive, new, or cool an office gadget might be, there is no magic bullet for solving all the problems that can come from inactivity or odd positioning over so many hours.</p>
<p>A good place to start is doing a full body scan while sitting in your normal position at your desk. How does your neck feel - achy? Tight? Are your legs falling asleep? Any muscle twitches? Tune into where you sense discomfort or repetitive, unnatural movement during daily tasks.</p>
<p>It is super important here to take into account the circumstances of your individual body. A workstation that might work for one person could be totally counterproductive or dysfunctional for someone else. Are you prone to back problems? Taller than most people? You might need to tweak the height of your desk or the tilt of your screen.</p>

    <h2 id="diet" class="scroll-mt-24">Diet</h2>
  <p>What it is it about working at a desk that utterly destroys healthy diets? If you work in an office with others, the breakroom snacks and lunch dates are obvious culprits. But those who work remotely aren’t immune either - if you regularly work from coffee shops or other coworking style cafes, you know how easy it is to add a not-so healthy treat in with your coffee order.</p>
<p>One way to get started is to get organized about eating. When we’re rushing to work or parked in front of the computer pushing to meet a strict deadline, it’s easy to grab things that will give us a quick boost but that aren’t that great for us. Having a plan for chaotic times is essential, whether it’s sliced fresh fruit in a desk drawer or portion-sized bags of trail mix in your bag. When making dinner, plan to make a little extra and set aside lunch the night before, making sure to tuck in lots of leafy greens and veggies as a side. We all know that processed foods aren’t good for our energy levels or overall health, so making it a habit to keep backups for stressful times is crucial.</p>

    <h2 id="beat-the-desk-life" class="scroll-mt-24">Beat the Desk Life</h2>
  <p>At the end of the day, our productivity is inextricably linked to our health. Especially if you make your own hours as a freelancer or work remotely, the battle for consistency and stability is real. Many times, the first things to get sacrificed are our diet, posture, and other healthy habits. But if you think about it, an investment in your health is an investment in your job satisfaction and ability to get things done.</p>
<p>Besides some of these general tips, here a few actual, actionable things that you can do to fight back against a sedentary lifestyle.</p>

    <h2 id="try" class="scroll-mt-24">Try</h2>
  
    <h3 id="exercise-at-your-desk" class="scroll-mt-24">Exercise at Your Desk</h3>
  <p>This one sounds pretty obvious, but mixing in 
    <a href="https://en.wikipedia.org/wiki/Isometric_exercise"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      isometric exercises
    </a>
   during your normal work day is a good way to stay fit. Choose one of 
    <a href="http://www.mensfitness.com/training/workout-routines/7-muscle-building-exercises-you-can-do-at-your-desk"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      these isometric exercises
    </a>
   and do one during a short break or every hour. If you work in a bigger office and don’t want to break a sweat in public, use mealtimes to take a walk around the block or climb up and down a flight of stairs in your building.</p>

    <h3 id="journaling" class="scroll-mt-24">Journaling</h3>
  <p>There are a million ways to keep yourself accountable to your health goals. If you’re keen on using visuals or like to jot things down manually, check out 
    <a href="https://www.buzzfeed.com/annaborges/bullet-journaling-for-health?utm_term=.wcbDdZ36n%23.xu43j8EaX"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      bullet journaling
    </a>
  . The beauty of this journaling system is that it’s endlessly adaptable to different productivity styles and needs. Track meals and snacks, or keep a “health to-do list” the same way you would track work tasks - anything from “drink 8 glasses of water” to “20 push ups.” Then, make it a goal to get through as many as you can.</p>
<p>If you’re not really the artsy type or prefer keeping things all in the cloud, add health reminders to the productivity platforms you already use. There are the old standards, like 
    <a href="https://evernote.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Evernote
    </a>
  , but you can also try the stripped-down 
    <a href="https://itunes.apple.com/us/app/wunderlist-to-do-list-tasks/id406644151?mt=8"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Wunderlist
    </a>
  . Create a separate list to make sure you step away from the desk, exercise, or grab a quick snack periodically.</p>

    <h3 id="automating-it" class="scroll-mt-24">Automating It</h3>
  <p>Use the tools you already have - set a timer on your smartphone or calendar for every hour you’re at your desk (just make sure to  for the meetings). Take just a few minutes to get away stretch, drink a glass of water, walk around, or do a few crunches. By essentially making an appointment with yourself, you increase the odds that you’ll stick to any health plan. You can also go with some time-boxing techniques like 
    <a href="https://en.wikipedia.org/wiki/Pomodoro_Technique"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Pomodoro
    </a>
   to ensure you take regular breaks between your tasks.</p>

    <h3 id="meditating" class="scroll-mt-24">Meditating</h3>
  <p>Meditation improves not only your mental health, but also gives your eyes a rest and gives you a chance to check in with your body. If you find yourself hunching over the computer, take just 5 or 10 minutes to close your eyes, relax, and focus on your breath. Try doing a body “scan” - just practice observing your body and your posture, and make adjustments (relax your shoulders, roll
your wrists) if you notice discomfort anywhere.</p>

    <h3 id="working-smarter" class="scroll-mt-24">Working Smarter</h3>
  <p>If you don’t always work in front of a computer, try re-organizing your daily routine so that your time at the computer is periodically broken up by other tasks. Batch similar tasks, like tracking your invoices or testing code, and then time yourself. If you feel like you’re getting sucked into the computer chair at the end of an hour, stand up and do a few stretches. Walk to a coworker&#39;s desk to have a quick chat instead of a text, or place your printer or other devices across the room so you have to get up in order to do certain tasks.</p>
<p>Working smarter also means streamlining your work process to minimize the time you’re in front of the screen overall. Evaluate the tools or software you use to get things done, and change things up if necessary. If you’re a writer and need a clean, distraction-free interface for getting creative or just taking notes, try chilled out 
    <a href="http://www.ommwriter.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      OmmWriter
    </a>
  . Or if you need a way to stay organized while doing web development work, try a text editor that streamlines the process with 
    <a href="http://www.vim.org/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Vim
    </a>
   (
    <a href="http://www.makeuseof.com/tag/top-7-reasons-to-give-the-vim-text-editor-a-chance/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      here’s a few reasons to love it
    </a>
  ).</p>

    <h2 id="buy" class="scroll-mt-24">Buy</h2>
  
    <h3 id="a-new-workspace" class="scroll-mt-24">A New Workspace</h3>
  <p>Standing desks are the hot new trend in startup office management (first they came for our cubicles, now it’s our chairs). Fans of standing desks (or other chairless workspaces like the “treadmill desk”) point to their benefits, like increased circulation and calorie burning - some go so far as to say that “
    <a href="http://www.huffingtonpost.com/the-active-times/sitting-is-the-new-smokin_b_5890006.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      sitting is the new smoking
    </a>
  .” 
    <a href="http://lifehacker.com/five-best-standing-desks-1528244287"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Lifehacker’s guide to standing desks
    </a>
   is a good place to start exploring your options. If getting a new desk is out of the question (or your budget), exercise balls and other “dynamic seats” are great for encouraging a more healthy seated posture.</p>
<p>The key with any workspace is to make sure it fits you as an individual. The cumulative effects of small movements, like pushing your neck forward to read your screen or overextending your wrists while typing, can negatively impact other parts of your body and cause spinal misalignments and muscle strains. Make sure you consider every area in your workspace by checking 
    <a href="http://www.mayoclinic.org/healthy-lifestyle/adult-health/in-depth/office-ergonomics/art-20046169"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      this doctor-approved guide
    </a>
  .</p>

    <h3 id="split-keyboards" class="scroll-mt-24">Split Keyboards</h3>
  <p>Generally, a good keyboard should mirror the way you actually hold your hands while you type. Split keyboards divide the keys between the hands, and usually are curved or tilt the keys inwards, supporting optimal wrist position. Negative tilt (where the bottom of the keyboard is pointed down towards you) also combats repetitive wrist strains. Broad, curved wrist supporters on Microsoft models are good 
    <a href="http://thewirecutter.com/reviews/comfortable-ergo-keyboard/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      if you’re on a budget
    </a>
  . Or check out the 
    <a href="https://www.ergodox.io/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      ErgoDox
    </a>
  , a blowout success on Kickstarter - each keyboard can be engineered and customized to every user, with key switches specifically designed for developers, writers, and anyone else who lives in front of their computers. You can also take a look at 
    <a href="https://ergodox-ez.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      ErgoDox EZ
    </a>
   for pre-assembled keyboards.</p>

    <h2 id="the-end" class="scroll-mt-24">The End</h2>
  <p>Though it’s difficult to put into practice, getting active is especially important for those that work in front of screens (which is honestly most of us nowadays). Working in a few exercises or updating our gear is ultimately an investment in our productivity and overall happiness - and at the end of the day, that’s really what it’s all about.</p>]]></description>
      </item>
    
      <item>
        <title>10 Things I learned about Elixir/Phoenix (as a Rubyist) while building my first Phoenix app</title>
        <link>https://devmystify.com/blog/10-things-i-learned-about-elixir-phoenix</link>
        <pubDate>Sun, 08 Jun 2025 15:47:34 GMT</pubDate>
        <description><![CDATA[<p>As explained in 
    <a href="https://devmystify.com/b/why-I-rebuilt-my-website-from-scratch-with-elixir-and-not-ruby"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      this post
    </a>
  , I made the switch from a slow Wordpress website to a blazing fast application built with Elixir and Phoenix. Now, I want to share the 10 things I learned about these technologies as a Rubyist.</p>

    <h3 id="1-functional-programming" class="scroll-mt-24">1. Functional Programming</h3>
  <p>Elixir is a functional language, which is quite a change when coming from the object-oriented Ruby where 
    <a href="https://www.ruby-lang.org/en/about/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      everything is an object
    </a>
  .
But what does it mean exactly?</p>
<p>If you don&#39;t know, functional programming is a 
    <a href="https://en.wikipedia.org/wiki/Programming_paradigm"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      programming paradigm
    </a>
  . The principal characteristic of this paradigm is the absence of side effects when calling a method. Most features of the Elixir language (immutable data, pattern matching, first-class functions and more) can be derived from the desire to prevent side-effects, as can be expected from a functional language.</p>
<p>Functional Programming is a wide and interesting topic, one I&#39;m not comfortable enough explaining. But if you want to take a closer look, I can recommend 
    <a href="https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-1-1f15e387e536#.t1xreotji"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      this article on Medium
    </a>
  .</p>

    <h3 id="2-immutability" class="scroll-mt-24">2. Immutability</h3>
  <p>The fact that data are immutable is a big change from Ruby. If you add an element to a list in Elixir, you won&#39;t be getting back the same list - a new one will have been created with the added element. However, a variable can be rebinded, which means you can change which data structure the variable points to/references in memory.</p>
<p>So you can do this...</p>
<pre><code class="language-elixir">x = %{} # x points to the immutable data %{}
x = %{one: &quot;two&quot;} # x now points to the immutable data %{one: &quot;two&quot;}
</code></pre><p>...but you cannot do the equivalent of this (written in Ruby):</p>
<pre><code class="language-ruby">x = {} # x points to the data {}
x[:one] = &quot;two&quot; # x still points to the same data,
                # but the data has been modified
</code></pre><p>It also means you cannot change data in loops; instead, <code>map</code> or <code>reduce</code> should be used. It&#39;s also a good example of how functions don&#39;t have side-effects and won&#39;t change the state of the program.</p>
<pre><code class="language-elixir">a = 0

Enum.each [1, 2, 3, 4], fn number -&gt;
  # We are not changing a, just creating new variables
  # limited to the scope of this anonymous function
  a = a + number
end

# Therefore a didn&#39;t change
IO.puts a # =&gt; 0
</code></pre><p>This is easily fixed with <code>reduce</code> - we can tell Elixir to calculate the sum using an accumulator (<code>acc</code>).</p>
<pre><code class="language-elixir">a = Enum.reduce [1, 2, 3, 4], 0, fn number, acc -&gt;
  number + acc
end

IO.puts a # =&gt; 10
</code></pre><p>The property of immutability has a number of advantages like data consistency and simpler concurrent programming (no need for locks!). It also makes it easier to reason about the code since functions are just about transforming their inputs into outputs, and not changing state.</p>

    <h3 id="3-pattern-matching" class="scroll-mt-24">3. Pattern Matching</h3>
  <p>I love pattern matching! I&#39;m still learning all the intricacies, but it&#39;s super useful to implement guard clauses (for example).
Whereas in Ruby, you might do something like this:</p>
<pre><code class="language-ruby">def foo(a)
  return 0 unless a &gt; 0
  2.0 / a
end

p foo(0) # 0
p foo(3) # 0.6666666666666666
</code></pre><p>In Elixir, you can just write:</p>
<pre><code class="language-elixir">defmodule Calculator do
  def foo(a) when a &gt; 0 do
    2 / a
  end
  def foo(_), do: 0
end

IO.puts Calculator.foo(0) # 0
IO.puts Calculator.foo(3) # 0.6666666666666666
</code></pre><p>And you can add as many definition as you need. Note that I used <code>_</code> for the second definition of <code>foo</code> because otherwise, Elixir would give me a warning for having an unused variable.</p>
<p>It can also be used to implement recursivity. Here is a relatively simple naive implementation of Fibonacci:</p>
<pre><code class="language-elixir">defmodule Fibonacci do
  def calc(0), do: 0
  def calc(1), do: 1
  def calc(n) when is_integer(n) and n &gt; 1 do
    calc(n - 1) + calc(n - 2)
  end
  def calc(_), do: {:error, &quot;Only positive integers allowed.&quot;}
end

IO.inspect Fibonacci.calc(10) # 55
IO.inspect Fibonacci.calc(&quot;foo&quot;) # {:error, &quot;Only positive integer allowed.&quot;}
</code></pre><p>We can do all sorts of things with pattern matching. Here, the <code>foo</code> function is returning a 
    <a href="http://elixir-lang.org/getting-started/basic-types.html#tuples"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      tuple
    </a>
  . When using it, I can pattern match with another tuple to extract the values from the tuple.</p>
<pre><code class="language-elixir">defmodule Foo do
  def foo do
    {&quot;foo&quot;, &quot;faa&quot;}
  end
end

# Thanks to pattern matching
{a, b} = Foo.foo
IO.puts a
IO.puts b
</code></pre><p>This pattern is often used to catch errors as you can see in the code below, using a 
    <a href="http://elixir-lang.org/getting-started/case-cond-and-if.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      case
    </a>
  .</p>
<pre><code class="language-elixir">case do_something_dangerous() do
  {:success, data} -&gt; # process data
  {:error, message} -&gt; # process error
end
</code></pre>
    <h3 id="4-supervisors-and-running-stuff-in-the-background" class="scroll-mt-24">4. Supervisors and running stuff in the background</h3>
  <p>If you&#39;re used to working with Ruby on Rails, you know that the right way to run long tasks (like sending a bunch of emails or generating a report) is to create a background job with 
    <a href="http://sidekiq.org/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Sidekiq
    </a>
   or 
    <a href="https://github.com/resque/resque"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Resque
    </a>
  .</p>
<p>With Phoenix (thanks to Elixir&amp;Erlang), there is absolutely no need for that - instead,concurrent processes (Elixir processes, not OS processes) can be spawned with 1 line of code (<code>spawn(fn -&gt; 2 * 2 end)</code>).</p>
<p>In order to manage these processes, Elixir provides the 
    <a href="http://elixir-lang.org/docs/stable/elixir/Supervisor.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Supervisor
    </a>
   module.</p>
<p>When generating a new Phoenix application, you will have the following supervisor configuration:</p>
<pre><code class="language-elixir"># lib/devblast.ex
defmodule Devblast do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      supervisor(Devblast.Endpoint, []),
      supervisor(Devblast.Repo, []),
      # Here you could define other workers and supervisors as children
      # worker(Devblast.Worker, [arg1, arg2, arg3]),
    ]

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Devblast.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    Devblast.Endpoint.config_change(changed, removed)
    :ok
  end
end
</code></pre><p>You can then add more supervisors and use them to run stuff in the background (I added one to handle small tasks like sending email, and another one as a memory cache).</p>
<pre><code class="language-elixir">children = [
  supervisor(Devblast.Endpoint, []),
  supervisor(Devblast.Repo, []),
  supervisor(Task.Supervisor, [[name: Devblast.TaskSupervisor]]),
]
</code></pre><pre><code class="language-elixir">Task.Supervisor.start_child Devblast.TaskSupervisor, fn -&gt;
  # Run some suff in the background
end
</code></pre><p>I learned how to do this thanks to Daniel and his great 
    <a href="http://blog.danielberkompas.com/2016/04/05/background-jobs-in-phoenix.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      article
    </a>
  . Kudos to him!</p>

    <h3 id="5-pipes-are-awesome" class="scroll-mt-24">5. Pipes are awesome</h3>
  <p>Familiar with *nix commands? Piping commands like <code>ls -a | grep *.ex</code>? Well, Elixir provides the same feature with the pipe operator.</p>
<p>This means you don&#39;t have to write code like this...</p>
<pre><code class="language-elixir">a = [1, [2], 3]
a = List.flatten(a)
a = Enum.map(a, fn x -&gt; x * 2 end)
</code></pre><p>...or like this:</p>
<pre><code class="language-elixir">a = Enum.map(List.flatten([1, [2], 3]), fn x -&gt; x * 2 end)
</code></pre><p>Instead, you can just pipe the output of each function and send it as the first argument of the following one with the pipe operator!</p>
<pre><code class="language-elixir">[1, [2], 3] |&gt; List.flatten |&gt; Enum.map(fn x -&gt; x * 2 end)
</code></pre><p><em>This example was taken from the 
    <a href="https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Elixir documentation
    </a>
  . I was lacking the imagination to find an example...</em></p>

    <h3 id="6-phoenix-doesnt-have-much-hidden-magic" class="scroll-mt-24">6. Phoenix doesn’t have much hidden magic</h3>
  <p>There is much less magic in Phoenix than there is in Rails. One of the things you really need to understand is the 
    <a href="https://hexdocs.pm/plug/Plug.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Plug
    </a>
   specification. In Phoenix, the entire HTTP layer is handled internally by a bunch of plugs (note that you can easily add your own!)</p>
<p>Here is a pipeline of plugs generated by Phoenix in the router file:</p>
<pre><code class="language-elixir">pipeline :browser do
  plug :accepts, [&quot;html&quot;]
  plug :fetch_session
  plug :fetch_flash
  plug :protect_from_forgery
  plug :put_secure_browser_headers
end
</code></pre><p>Each one of these plugs will receive the request object (<code>conn</code>) and transform it.</p>

    <h3 id="7-you-need-to-learn-how-brunch-works" class="scroll-mt-24">7. You need to learn how Brunch works</h3>
  <p>The Phoenix team decided to use an existing build tool for the assets, which is not a bad idea. They went with 
    <a href="http://brunch.io/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      brunch.io
    </a>
  , which is pretty simple and easy to use, as long as you go through their 
    <a href="http://brunch.io/docs/getting-started"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      documentation
    </a>
  .</p>

    <h3 id="8-huge-lack-of-tutorials" class="scroll-mt-24">8. Huge lack of tutorials</h3>
  <p>There is still a huge lack of tutorials and articles on Elixir and Phoenix. I had some issues for which I wasn&#39;t able to find any help, and had to spend a lot of time figuring on my own. I&#39;m planning to write some stuff about them to fill the hole.</p>

    <h3 id="9-edeliver-and-exrmdistillery-for-deployments" class="scroll-mt-24">9. edeliver and exrm/distillery for deployments?</h3>
  <p>The commonly accepted way to deploy Phoenix applications is with 
    <a href="https://github.com/boldpoker/edeliver"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      edeliver
    </a>
   and either 
    <a href="https://github.com/bitwalker/exrm"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      exrm
    </a>
   or 
    <a href="https://github.com/bitwalker/distillery"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      distillery
    </a>
  . edeliver lets us perform hot-code upgrades which is a cool way to upgrade your application without restarting it.</p>
<p>However, I was disappointed by how slow the deployment was. Maybe I did something wrong, but the generated release would take hours to get copied to the production server...</p>
<p>So instead, I decided to go with 
    <a href="https://github.com/dokku/dokku"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Dokku
    </a>
   and I&#39;m quite happy with it, even if I don&#39;t have hot-code upgrades!</p>

    <h3 id="10-frameworks-should-have-unique-name" class="scroll-mt-24">10. Frameworks should have unique name.</h3>
  <p>Alright, this last point is a bit of a joke - don&#39;t take it too seriously. I really had this issue with Rails ,but it happened to me more than once with Phoenix: there are too many things named Phoenix!</p>
<p>I can&#39;t tell how often I searched for something in Google using &quot;phoenix&quot; and my query and received results about 
    <a href="https://phoenix.apache.org/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Apache Phoenix
    </a>
   or the 
    <a href="https://en.wikipedia.org/wiki/Phoenix,_Arizona"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      city of Phoenix
    </a>
  .</p>
<p>Phoenix is not unique enough :&#39;)</p>

    <h3 id="the-end" class="scroll-mt-24">The End</h3>
  <p>I still have many, many things to learn, but I just wanted to share some of the first things I’ve encountered in the past weeks. I will be writing more on my experience with Elixir and Phoenix shortly, so don&#39;t forget to 
    <a href="https://devmystify.com/join"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      subscribe to the newsletter
    </a>
   or the 
    <a href="https://devmystify.com/blog/feed"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      RSS feed
    </a>
  .</p>
<p><em>I&#39;m still a beginner in Elixir so all the information in this article might not be 100% accurate. If you find anything wrong, please leave a comment to let me know.</em></p>]]></description>
      </item>
    
      <item>
        <title>What are HTTP Resources?</title>
        <link>https://devmystify.com/blog/what-are-http-resources</link>
        <pubDate>Sun, 08 Jun 2025 15:42:34 GMT</pubDate>
        <description><![CDATA[<p>Resources are mentioned in the HTTP and URI RFCs and are concepts defined by the REST architectural style in the <code>Uniform Interface</code> constraint. In this article, extracted from 
    <a href="http://master-ruby-web-apis.samurails.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Master Ruby Web APIs
    </a>
  , we are going to define what a resource is exactly.</p>
<p>You probably already have an idea of what a resource is. The term has been used widely, and most of the times pretty accurately, in different web technologies.</p>
<p>After being defined as a part of the REST style, it was pragmatically introduced as the target of URLs (Uniform Resource Locators - 
    <a href="https://tools.ietf.org/html/rfc1738"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      RFC 1738
    </a>
  ) before being extended to URIs (Uniform Resource Identifier 
    <a href="https://tools.ietf.org/html/rfc3986"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      RFC 3986
    </a>
  ) and IRI (Internationalized Resource Identifier - 
    <a href="https://tools.ietf.org/html/rfc3987"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      RFC 3987
    </a>
  ).</p>
<p>Before we continue on resources, let&#39;s define what these acronyms mean exactly.</p>

    <h2 id="uri-vs-url-vs-urn" class="scroll-mt-24">URI vs URL vs URN</h2>
  <p>It is important to understand the difference between URL, URI, IRI and URN because they represent different concepts. 
    <a href="https://www.w3.org/People/Berners-Lee/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Tim Berners-Lee
    </a>
   defined in 
    <a href="https://tools.ietf.org/html/rfc3986"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      RFC 3986
    </a>
   a &quot;Uniform Resource Identifier (URI)&quot; as &quot;a compact sequence of characters that identifies an abstract or physical resource&quot; and claimed that a &quot;URI can be further classified as a locator, a name, or both.&quot;</p>
<p>He also stated that the term &quot;Uniform Resource Locator&quot; (URL) refers to the subset of URIs that, in addition to identifying a resource, provide a means of locating the resource by describing its primary access mechanism (e.g., its network &#39;location&#39;).</p>
<p>Got it?</p>
<p>Let me simplify this for you. Let&#39;s say you are a resource -</p>
<ul>
<li>URLs are locators (hence the name). A URL is your home address: 12 Whatever Street, 11002, Paris.</li>
<li>URNs are names. For you, a URN is your name: John Smith.</li>
<li>URIs can be a locator (URL), a name (URN), or both.</li>
</ul>
<p><strong>Basically, all URLs are URIs, but not all URIs are URLs.</strong></p>
<p>The only thing that can qualify a URI as a URL is if the latter includes how to find the former. <code>www.google.com/account</code> is not a URL, just a URI. However, <code>http://www.google.com/account</code> is a URL (and still a URI) because it includes the protocol (<code>http://</code>) that can be used to access the resource (<code>www.google.com/account</code>).</p>
<p>URNs are much simpler - they are just names.</p>
<p>An ISBN (like <code>978-0553293357</code>) is a unique identifier for a book, which makes it a URI, and more specifically, a URN.</p>
<p>Finally, IRIs are an extension of URIs meant to include characters from the Universal Character Set (Japanese Kanji, for example). Indeed, URIs are limited to a subset of the ASCII character set.</p>

    <h2 id="http-and-uris" class="scroll-mt-24">HTTP and URIs</h2>
  <p>In the scope of 
    <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      HTTP
    </a>
  , URIs are &quot;simply formatted strings which identify - via name, location, or any other characteristic - a resource.&quot;</p>
<p>A <code>resource</code> is the <code>thing</code> living on the other side of a URI and a URI only points to one resource. That sounds rather abstract, so let&#39;s look at the following example.</p>
<pre><code>http://example.com/users
</code></pre><p>This URL points to a resource named <code>users</code>. Note that we can never retrieve this resource; instead, we can only get a representation of it as defined by the <code>Uniform Interface</code> constraint. So we can say that a resource never changes, only its representations do.</p>
<p>So how do we name resources? After all, resources are just abstract concepts pointed at by URIs. In theory, it can be anything since naming doesn&#39;t matter to the machine that will be the final client of your web API.</p>
<p>But we must also consider that we are building for human beings and we are still far away from removing developers from the equation. Since we are building web APIs for humans, who will use them to implement their client code, we should use concepts and semantics that they will understand. This follows the same principle used in naming resources for websites.</p>
<p>The following is based on the best practices to build an easy-to-use API, and not really on web standards.</p>
<p>If I want to represent the concept of a list of users, I would just use the pluralized word <code>users</code>, which is just a noun. Making a request to this URL would return a representation listing a bunch of users. Anyone would be able to understand this, and seeing the representation will just confirm their idea about this resource.</p>
<p>With the URL below, we are getting a list of users.</p>
<pre><code>http://example.com/users
</code></pre>
    <h2 id="verbs-vs-nouns" class="scroll-mt-24">Verbs VS Nouns?</h2>
  <p>But shouldn&#39;t we call it <code>http://example.com/get_users</code>, so people understand it more easily?</p>
<p>The answer is no, but the question itself is not stupid. We just don&#39;t have to include this <code>get</code> in the URL because HTTP has us covered already. Thanks to HTTP methods, we are able to extract as much as possible from the URI pointing to a resource. The best practice here is to use only nouns for resources, and not include meaningless verbs or words.</p>
<p>Some people prefer to use singular names for their resources, like <code>user</code>. This choice is up to you, but I recommend simply using the plural version. Whatever you decide, stick to it. Don&#39;t use <code>/users</code> and <code>/user/:id</code> in the same API - it&#39;s just confusing for developers. Don&#39;t forget that your goal is to build something easy-to-use <strong>for them</strong>, not you. Think about them before thinking about what you prefer.</p>

    <h2 id="going-further" class="scroll-mt-24">Going further</h2>
  <p>As said at the beginning of this article, you&#39;ve just read an extract from 
    <a href="http://master-ruby-web-apis.samurails.com/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Master Ruby Web APIs
    </a>
  . If you&#39;d like to learn more about HTTP and how to build web APIs with Ruby, feel free to check it out. While the book is targeted at web APIs, you will still learn a ton of ways to improve regular web applications.</p>]]></description>
      </item>
    
      <item>
        <title>Automated testing is fun</title>
        <link>https://devmystify.com/blog/automated-testing-fun</link>
        <pubDate>Sun, 08 Jun 2025 15:37:34 GMT</pubDate>
        <description><![CDATA[<p><strong>Testing is the only way to ensure that your code works.</strong></p>
<p>That&#39;s it, I said it. It&#39;s the only thing you need to remember from this post. If you&#39;re working with code, you are already somehow <strong>testing</strong>, right?</p>
<p>Maybe you do it by hand, you know, manually checking that the feature you just added works correctly. That&#39;s a good start!</p>

    <h2 id="the-problem-with-manual-testing" class="scroll-mt-24">The problem with manual testing</h2>
  <p>The problem is that manual testing doesn&#39;t scale. Let&#39;s say you have an automated email being sent out anytime a user signs up. Are you doing it by hand? Of course not, because <em>it doesn&#39;t scale</em>. If you have one new user per day, it might be fine (still, it would be a pain, let&#39;s be honest).</p>
<p>When you have 10, 100 or even 100,000 new users every single day, you clearly cannot handle it manually anymore, so you just write code to deal with it. Now, let&#39;s go back to testing. The example we just saw is a bit extreme when we&#39;re talking about testing, I&#39;ll admit it. Still, testing one feature is fine. Once you&#39;ve added 20, 30 or more features to your application, are you still going to test them all when you change a piece of code?</p>
<p>Your answer is probably no. Personally, I wouldn&#39;t do it (and I didn&#39;t back in the days) because, well, it would take forever. I&#39;d just test the latest feature I implemented, and maybe one that&#39;s related. But I wouldn&#39;t realize that X broke in the Y feature. Damn. Quick deploy on a Friday afternoon for a quick adrenaline rush, and off for a weekend at the beach. What I wouldn&#39;t realize is the broken checkout process - a colossal loss of $1 billion over the weekend.</p>
<p>Scary story, huh?</p>
<p>It&#39;s actually inspired from a real story, but the numbers have been inflated. A lot. Else they would have fired me!</p>
<p>Manual testing has its limits - our limits.</p>
<p><strong>Everything is getting automated in our lives, why not automate the testing of our applications? You know, the applications that we created to automate something else that was too annoying to do manually.</strong></p>
<p>Enter automated testing. It&#39;s nothing new. Been around since people started writing code.</p>

    <h2 id="wait-i-dont-know-what-is-automated-testing" class="scroll-mt-24">Wait... I don't know what is "automated testing"!</h2>
  <p>Alright. Let&#39;s me give you a quick explanation. The idea behind automated testing is to write a piece of code that only has one responsibility: ensure that a specific functionality in your application works as expected.</p>
<p>Automated testing includes the process of writing unit tests and integration tests. While unit tests are meant to test only &quot;one unit&quot; (a single component), integration tests are used to test multiple units, and how they interact together.</p>
<p>Now, let&#39;s take a look at some concrete examples. Say we want to test the <code>#add</code> method in the <code>Calculator</code> class below.</p>
<pre><code class="language-ruby"># calculator.rb
class Calculator
  def add(a, b)
    a + b
  end
end
</code></pre><p>And here is the method we would use to test that the <code>#add</code> method works correctly. We first setup an instance of the <code>Calculator</code> class, before using <code>asset_equal</code> (provided by 
    <a href="https://github.com/seattlerb/minitest"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Minitest
    </a>
  ), to check that calling <code>calculator.add(2, 4)</code> returns <code>6</code> as expected.</p>
<pre><code class="language-ruby">def test_add
  calculator = Calculator.new
  assert_equal 6, calculator.add(2, 4)
end
</code></pre><p>Here is the complete Minitest example, for reference.</p>
<pre><code class="language-ruby">require &quot;minitest/autorun&quot;
require &quot;calculator&quot;

class TestCalculator &lt; Minitest::Test
  def test_add
    calculator = Calculator.new
    assert_equal 6, calculator.add(2, 4)
  end
end
</code></pre><p>Now that you have an idea of what a test looks like, let&#39;s talk about why you would want to write them exactly.</p>

    <h2 id="why-write-tests" class="scroll-mt-24">Why write tests?</h2>
  <p>Spending time writing automated tests may seem counter-productive when you&#39;re trying to push features out as fast as possible. What you need to realize is that automated tests are an incredible useful tool that have a huge range of benefits:</p>
<ul>
<li>Save time &amp; money</li>
<li>Once you&#39;re good enough, you can actually develop faster by reducing the time you spend interacting with a web browser.</li>
<li>The tests provide instant feedback on your code, allowing quick refactoring, forcing you to write code in a smarter way and reducing the coupling between your components.</li>
<li>Tests will allow you to catch bugs early on, before pushing anything to production.</li>
<li>A good set of tests for an application makes it super easy to introduce new comers to the codebase. They can play around, try to fix bugs or add features and the test suite will tell them if they broke anything.</li>
<li>Tests give you confidence in your code.</li>
<li>A test suite is an always up-to-date documentation for your code. If someone wants to know how a piece of code works, they can just checkout the logic in the tests.</li>
</ul>
<p>Even if these don&#39;t sound good enough for you, wait until you try it to make a judgement ;)</p>

    <h2 id="going-further" class="scroll-mt-24">Going further</h2>
  <p>Learning how to write tests takes longer than reading an article on the topic. If you&#39;re really serious about making your code better, faster and stronger, you might want to checkout this course I&#39;m building: 
    <a href="http://courses.samurails.com/rails-5-testing-why-where-how/"
       
       target="_blank" rel="noopener noreferrer"
       class="text-orange-500 hover:text-orange-400">
      Rails 5 Testing - Why, Where and How?
    </a>
  ,</p>]]></description>
      </item>
    
  </channel>
</rss>