<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Antoine Coulon</title>
    <description>The latest articles on DEV Community by Antoine Coulon (@antoinecoulon).</description>
    <link>https://dev.to/antoinecoulon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F702314%2Fecb88ea8-6968-4326-82d1-8c9a97273a30.jpeg</url>
      <title>DEV Community: Antoine Coulon</title>
      <link>https://dev.to/antoinecoulon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antoinecoulon"/>
    <language>en</language>
    <item>
      <title>🚨Avoid this mistake when running containerized applications in production</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Mon, 06 Nov 2023 16:30:00 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/avoid-this-when-running-containerized-applications-in-production-562k</link>
      <guid>https://dev.to/antoinecoulon/avoid-this-when-running-containerized-applications-in-production-562k</guid>
      <description>&lt;p&gt;Hello everyone!&lt;/p&gt;

&lt;p&gt;Let's talk about things we &lt;strong&gt;must manage when running containerized applications&lt;/strong&gt; and how this relates to proper management of &lt;strong&gt;termination signals&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you prefer the GitHub with examples format, &lt;a href="https://github.com/antoine-coulon/running-containerized-apps"&gt;I created a repository that follows the same track with practical examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before specifically talking about containers, let's put them aside and see how we run applications on a daily basis. We all use various operating systems that can run huge amount of tasks. These tasks are executed within &lt;strong&gt;processes&lt;/strong&gt;, one of the fundamental units of an operating system. &lt;/p&gt;

&lt;p&gt;Consequently each application is assigned to a process that will live until the application properly terminates.&lt;/p&gt;

&lt;p&gt;With Node.js, we can check what is the process id currently running our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"console.log(process.pid)"&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 39829
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall, one of the core responsabilities of an operating system is to manage the life cycle of these processes, whether they should be created, killed, paused, restarted, etc.&lt;/p&gt;

&lt;p&gt;Processes can also interact with other processes, for instance it's pretty common for a parent process to manage a set of child processes to run a multi-process application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Termination signals
&lt;/h2&gt;

&lt;p&gt;Termination signals are the primitive used by the OS to tell a specific process to terminate.&lt;/p&gt;

&lt;p&gt;On Unix, you can send these termination signals through the &lt;strong&gt;kill&lt;/strong&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;kill &lt;/span&gt;39829 // SIGTERM by default
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; 39829 // SIGKILL 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are many &lt;a href="https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html"&gt;existing termination signals&lt;/a&gt; but we'll talk about the three most widely used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SIGTERM&lt;/strong&gt;: this is the most common termination signal. This termination is called "soft" because the signal simply orders the process to stop but the process in question can decide to ignore it. This is the signal used by default on Unix OS, when using the &lt;code&gt;kill&lt;/code&gt; command. This is the equivalent of the OFF button on your remote control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SIGKILL&lt;/strong&gt;: this is the most brutal termination signal because it does not allow the target process to react to or ignore the signal. This signal is used to terminate the process immediately and by definition does not allow a graceful shutdown of the application (explained a little further down). It is the equivalent of suddenly unplugging your power cable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SIGINT&lt;/strong&gt;: this is the interrupt signal which is for example sent when the user sends CTRL+C command in the terminal currently executing the process.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Graceful shutdown: one of the main responsabilities of a production-grade application
&lt;/h2&gt;

&lt;p&gt;Now that we know what termination signals are, we could wonder what is the responsibility of the application regarding these signals?&lt;/p&gt;

&lt;p&gt;Well, the application itself must properly handle these signals, otherwise there is no room for &lt;strong&gt;graceful shutdown&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;graceful shutdown&lt;/strong&gt; for an application refers to a clean and controlled program termination where there is no data loss nor leak of resources and where   the program has the ability to perform other types of critical operations before exiting, such as logging. This is performed by the "Eject" action of a computer drive that allows a clean disconnect. I'm assuming you already know the consequences when doing that 💣.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;How to perform a graceful shutdown?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most of the time, process supervisors will first emit signals that are "soft" such as &lt;strong&gt;SIGTERM&lt;/strong&gt; or &lt;strong&gt;SIGINT&lt;/strong&gt; which don't terminate right away processes.&lt;/p&gt;

&lt;p&gt;During a specific period of time (depending on the supervisor), the application process will have time to clean up everything it has to do and then exit i.e. performing the graceful shutdown itself. In other words, graceful shutdown begins with reacting to a termination signal and then trigger application level code to manage the teardown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Node.js&lt;/span&gt;

&lt;span class="c1"&gt;// Graceful shutdown on SIGTERM&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// close server, close database connection, write logs...&lt;/span&gt;
 &lt;span class="nx"&gt;releaseAllResources&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="c1"&gt;// then manually exit&lt;/span&gt;
 &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;143&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see to handle these signals, we can attach process handlers with our own custom logic. Note that it's important for the handler to exit the process manually after the clean up, otherwise the process will hang and still live.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Node.js&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// If we don't manually exit, the process will hang forever &lt;/span&gt;
 &lt;span class="c1"&gt;// until a SIGKILL is fired. Once attaching these types of handler, &lt;/span&gt;
&lt;span class="c1"&gt;// it's then your responsibility to release the last ressource: the process itself.&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding &lt;strong&gt;SIGTERM&lt;/strong&gt; process handlers allows us to take control over the logic that will be performed, but we're also responsible to properly exit after that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We could also be tempted to not exit after and keep the process running, but this is really disadvised and the handler should only be used for the graceful shutdown itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;But can't attaching these handlers be harmful if we don't exit after?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Of course it can be! This is why most of the supervisors usually don't only rely on &lt;strong&gt;SIGTERM&lt;/strong&gt; and after firing &lt;strong&gt;SIGTERM&lt;/strong&gt; they wait until a certain period of time and send &lt;strong&gt;SIGKILL&lt;/strong&gt; if the process was still not terminated (this is essentially the difference between "Quit" and "Force Quit" when using Task Manager). Note that depending on the OS, some can just let the process hang indefinitely. &lt;/p&gt;

&lt;p&gt;Consequently even applications on your own host machine should properly handle these signals, not only the ones in production, even though the consequences will most likely be less serious and cost less money.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production environment
&lt;/h2&gt;

&lt;p&gt;Let's come back on the main, topic, &lt;strong&gt;running containerized applications in production-grade environments&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For this article, I consider the fact that you know what a container is. If you don't, please check &lt;a href="https://www.docker.com/resources/what-container/"&gt;Docker's documentation&lt;/a&gt; before going deeper into this subject.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Life cycle of a container
&lt;/h2&gt;

&lt;p&gt;For most production systems, applications will be deployed and run into containers, allowing applications to run in a sandboxed and lightweight environment. There are many different types of containers, but we won't go into these details in this series as most of the good practices and things to avoid can be applied to all types of containers.&lt;/p&gt;

&lt;p&gt;Containers are usually managed by supervisors or orchestrators that determine whether a container should be started, restarted, stopped, scaled up/down etc. One of the most famous container orchestrator is &lt;a href="https://kubernetes.io/"&gt;Kubernetes (K8S)&lt;/a&gt; originally developed by Google.&lt;/p&gt;

&lt;p&gt;The goal of K8S is to orchestrate containers at scale, managing clusters of containers including lot of different services and applications of a company. K8S in a nutshell manages containers deployments, containers life cycles by determining whether they need to be scaled up or down based on the load, but also deals with deployments and when to take arbitrary decision like stop/restart them when new versions of containers are available.&lt;/p&gt;

&lt;p&gt;For Kubernetes to be working efficiently, one fundamental criteria for containers has to be respected: &lt;strong&gt;an application running in a container should properly react to signals coming from the orchestrator&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the danger of not reacting properly to these signals in the context of containers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As it was described in the previous section for OS processes, the supervisor, which is Kubernetes in the context of containers, will first try to send &lt;strong&gt;SIGTERM&lt;/strong&gt; and after the default value of 30 seconds (can be customized) is elapsed, a &lt;strong&gt;SIGKILL&lt;/strong&gt; signal will be sent to be sure that the container is shut down.&lt;/p&gt;

&lt;p&gt;Here a list of container services with their respective behavior with SIGTERM and SIGKILL&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Termination signal behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Elastic Container Service&lt;/td&gt;
&lt;td&gt;SIGTERM, wait 30s, then SIGKILL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;SIGTERM, wait 30s, then SIGKILL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure App Service&lt;/td&gt;
&lt;td&gt;SIGTERM, wait 30s, then SIGKILL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;SIGTERM, wait 10s, then SIGKILL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We could say that it's fine and we don't really care about handling termination signals as the &lt;strong&gt;SIGKILL&lt;/strong&gt; will be sent anyway at some point and will terminate both our application process and the container initially hosting the app process.&lt;/p&gt;

&lt;p&gt;Putting the &lt;strong&gt;graceful shutdown&lt;/strong&gt; process aside that we already explained in the previous section, there is also another problem that can become critical in most production systems, which is &lt;strong&gt;latency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine that a container needs to be rollbacked because a bug was introduced, it means that during 30s we won't be able to do anything else but waiting. Also, all operations such as scale down or stop will keep using resources until the &lt;strong&gt;SIGKILL&lt;/strong&gt; was fired. At scale on hundreds/thousands of containers, 30s can quickly become a lot of overhead and introduce performance critical penalties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remember to attach termination signal handlers to your applications and handle graceful shutdowns properly. This will help both the application and all the production systems build around it to be more reliable, resilient and effective.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ensure that termination signals can be propagated
&lt;/h2&gt;

&lt;p&gt;Usually, when your application properly manages terminal signal, it's already a good point. However, there is a prerequisite to that: the termination signal must be propagated correctly starting for the supervisor at the very top to the very bottom where your application process resides.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What could go wrong?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's take a very simple example with Docker containers, where a &lt;code&gt;Dockerfile&lt;/code&gt; specifies an &lt;strong&gt;ENTRYPOINT&lt;/strong&gt; such that it spawns a shell process as the PID1, known as the &lt;strong&gt;shell form&lt;/strong&gt; of the &lt;strong&gt;ENTRYPOINT&lt;/strong&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please don't use the following Dockerfile for shipping Node.js applications in production. This is a simplified example that will pull a deprecated Node.js version (v12) that only suits for educative purposes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can test all the examples that will follow on your own using &lt;a href="https://github.com/antoine-coulon/running-containerized-apps"&gt;the repository I created&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:22.04&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; node index.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Building your own Node.js image is discouraged, here we build it from Ubuntu for educative purposes. Please rely on official images for production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will result in two processes in the container, shell being the parent one and the Node.js application being a child process of the shell (subshell). The consequence is that &lt;strong&gt;shell&lt;/strong&gt; does not propagate termination signals correctly, meaning that the Node.js process will never receive these signals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Command run within the container&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux
USER       PID  COMMAND
root         1  /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; node index.js
root         7  node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we do &lt;code&gt;docker stop &amp;lt;container-id&amp;gt;&lt;/code&gt; we can see that the Node.js process does not receive the SIGTERM signal and keeps living until Docker sends the SIGKILL signal after 10s.&lt;/p&gt;

&lt;p&gt;One way to circumvent that is to avoid shell to be PID1 and turn the application process itself to PID1 using the &lt;strong&gt;exec form&lt;/strong&gt; of the ENTRYPOINT.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:22.04&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node", "index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run again &lt;code&gt;ps aux&lt;/code&gt; within the container we can see that the Node.js process is now PID1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux
USER       PID  COMMAND
root         1  node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because our application follows the good practices we talked about in the first part, it correctly handles signals and is able to perform graceful shutdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't let your application process be PID1
&lt;/h2&gt;

&lt;p&gt;The good part of having our application process being the only process in the container and given that our application has setup the expected process handlers is that we are able to properly manage termination signals.&lt;/p&gt;

&lt;p&gt;Nonetheless, we have a new problem, which is that our application now is the PID1 also known as init process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is PID1 alias "init" process?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PID1 or "init" process has very precise responsibilities regarding the operating system (or more precisely in the container in that context).&lt;/p&gt;

&lt;p&gt;Before explaining the issue around PID1, let's quickly return to the basis of an OS with the organization of processes.&lt;/p&gt;

&lt;p&gt;The process register is represented as a graph, where PID1 represents the root node, commonly called "init".&lt;/p&gt;

&lt;p&gt;Unlike other processes, the "init" process (PID1) is assigned very specific responsibilities by the Kernel, which are in particular:&lt;/p&gt;

&lt;p&gt;initialize all services (processes) required by the operating system.&lt;/p&gt;

&lt;p&gt;manage and reap so-called “zombie” or “orphan” processes. A "zombie" process is a process that has finished executing but for which resources continue to be monopolized.&lt;/p&gt;

&lt;p&gt;ensure that child process' exit code is forwarded properly outside of the container.&lt;/p&gt;

&lt;p&gt;All of these responsibilities cannot and should not be shouldered by your application nor the runtime your application is being executed on (JVM, Node.js, etc).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who should be PID1 then?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of solutions out there but Tini is the most famous and battle-tested one.&lt;/p&gt;

&lt;p&gt;Tini has one goal, which is to provide an “init” process that works as expected. It is an independent executable, but it is important to mention that it is embedded by default in Docker since v1.13, usable with &lt;code&gt;docker run --init&lt;/code&gt; or with docker-compose (v2.2) using &lt;code&gt;init: true&lt;/code&gt; from the config file for a service.&lt;/p&gt;

&lt;p&gt;Still in the context of a Node.js application, here is an example of a minimalist but closer to production-ready Dockerfile using Tini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; tini

&lt;span class="c"&gt;# Copy app files&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/sbin/tini", "--", "node", "index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully you are now aware of some of the traps that you should avoid when running containerized apps in production and more generally understand the way processes communicate and how supervisors manage their life cycle.&lt;/p&gt;

&lt;p&gt;More resources linked to this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/"&gt;Docker and the PID1 problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/containers/graceful-shutdowns-with-ecs/"&gt;Amazon ECS, how to graceful shutdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/krallin/tini"&gt;Tini, a useful process manager for containerized apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I frequently publish blog posts about software engineering, don't forget to subscribe if you're interested in discovering more!&lt;/p&gt;

&lt;p&gt;See you later 👋🏻&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>container</category>
      <category>docker</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>🖼️ Unleash graph visualization power to take full control over your project with skott</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Mon, 02 Oct 2023 09:30:38 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/unleash-graph-visualization-power-to-take-full-control-over-your-project-with-skott-3cm2</link>
      <guid>https://dev.to/antoinecoulon/unleash-graph-visualization-power-to-take-full-control-over-your-project-with-skott-3cm2</guid>
      <description>&lt;p&gt;Hello everyone, exciting announcement about skott, the web application automatically embedded has now reached v2, after several months of development and after many issues were opened by the community asking for improvements!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you never heard of skott, here is the first &lt;a href="https://dev.to/antoinecoulon/introducing-skott-the-new-madge-1bfl"&gt;blog post introducing the tool&lt;/a&gt;. The API is not really up-to-date and much more feature were added since then but the introduction still remains valid!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;skott is all-in-one devtool to automatically analyze, search and visualize dependencies from your JavaScript and TypeScript projects&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation and quick run
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install skott -g
skott --displayMode=webapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a pretty quick overview of what you can achieve with the new version of the web application:&lt;/p&gt;

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

&lt;p&gt;Graph Visualization is hard, and being able to extract valuable information from graphs with hundreds or thousands of nodes and edges is not an easy thing.&lt;/p&gt;

&lt;p&gt;In that web application v2, the focus was on improving the experience dealing with the information that was generated by skott.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graph configuration
&lt;/h2&gt;

&lt;p&gt;Probably the most asked section! One of the biggest drawbacks of the v1 of the web application was its inability to render graphs accordingly to their sizes, shapes, and user preferences. Graph Visualization is complex because depending on the size and on the shape of the links between all the nodes, some layouts and parameters might fit better. Because it's hard to automatically determine the best configuration out there for anyone's project, I made the choice to offer some customization over graph rendering, let's see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cluster layout&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cluster layout builds graph based on connections between nodes. Especially useful when having a graph with a lot of interconnected nodes, including circular dependencies.&lt;/p&gt;

&lt;p&gt;Three node spacing algorithms are currently available: "Barnes Hut", "Repulsion", "Force Atlas 2". These are all provided by &lt;a href="https://github.com/visjs" rel="noopener noreferrer"&gt;vis-js&lt;/a&gt;, the library skott uses to draw graphs.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hierarchical layout&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hierarchical arranges nodes in a hierarchical fashion. It helps visualizing graph structures that naturally use some sort of hierarchical architecture that is no circular dependencies between parent and children nodes.&lt;/p&gt;

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

&lt;p&gt;Note that depending on graph size, some layouts, algorithms or options might perform and fit better. For instance "Improve Spacing" option improves node spacing and layout rendering but takes up to 100x more time to render the graph.&lt;/p&gt;

&lt;p&gt;In most cases, what you want is to reduce the dataset upfront to avoid rendering huge graphs that will both be a costly operation but also will be harder to visualize correctly. &lt;/p&gt;

&lt;h2&gt;
  
  
  File Explorer
&lt;/h2&gt;

&lt;p&gt;File Explorer section provides a VSCode like explorer to browse files and directories. The whole purpose is to have an easy way to navigate through the graph by recreating the file tree as it would appear in an IDE. It also provides ways of filtering folders (either via the glob pattern or command actions available on each folder) and focusing on files.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

&lt;p&gt;Section that allows to enhance the rendered graph by highlighting &lt;strong&gt;circular&lt;/strong&gt;, &lt;strong&gt;third-party&lt;/strong&gt;, or &lt;strong&gt;builtin&lt;/strong&gt; dependencies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As of today this section just ported the existing features that were initially introduced in web application v1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This section is pretty minimalist for now but it has a lot of potential, such as displaying &lt;strong&gt;unused dependencies&lt;/strong&gt; (unused npm dependencies are already collected by skott itself).&lt;/p&gt;

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

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

&lt;p&gt;Summary sections aims to provide global stats about the project itself, number of files traversed, number of circular dependencies, exhaustive lists of third-party dependencies (npm) and builtin (Node.js) dependencies used throughout the project, etc.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;More to come!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot of information collected by skott is not yet displayed in the web application, but it's only a matter of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cycles explorer
&lt;/h2&gt;

&lt;p&gt;I think this will be a super useful section to fight against circular dependencies. Currently, skott only highlights nodes involved in circular dependencies. However, highlighting is not enough and can be misleading when you want to have a clear and granular vision about each independent cycle.&lt;/p&gt;

&lt;p&gt;This section shape is not settled yet so don't hesitate to suggest features if you have ideas that could be useful for you in mind!&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive playground
&lt;/h2&gt;

&lt;p&gt;skott provides a &lt;a href="https://github.com/antoine-coulon/skott/tree/main/packages/skott#graph-api" rel="noopener noreferrer"&gt;Graph API&lt;/a&gt; that can help recreating sub-graphs where only direct or deep nodes and parent/children are involved.&lt;/p&gt;

&lt;p&gt;This would provide capabilities to focus on specific modules and recreate the sub graph that these modules are involved in, reducing the overall noise that can be introduced by drawing the whole graph.&lt;/p&gt;

&lt;p&gt;Thanks for reading! If you ever come to use skott, share your feedback, open issues/discussions, I'm trying to make the project very interactive and reactive to contributions. Also if you like it, don't forget to drop ⭐️ :) &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>visualization</category>
    </item>
    <item>
      <title>💉 Test-Driven Development and Dependency Injection are the way</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Wed, 26 Jul 2023 10:04:40 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/test-driven-development-and-dependency-injection-are-the-way-cib</link>
      <guid>https://dev.to/antoinecoulon/test-driven-development-and-dependency-injection-are-the-way-cib</guid>
      <description>&lt;p&gt;Welcome back into these series everyone, past few days have been incredible as &lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;skott&lt;/a&gt; just reached 130 stars on GitHub, 100k of total downloads and lately around +12k weekly downloads since I started open-sourcing it. It's very far for being mainstream but it's a good start, isn't it? Anyway, let me put my personal satisfaction aside, and let's talk about what you came for!&lt;/p&gt;

&lt;p&gt;For the newcomers of the series, let me do a quick &lt;code&gt;skott&lt;/code&gt; introduction to put things in context.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;skott&lt;/code&gt; is a all-in-one devtool to automatically analyze, search and visualize dependencies from JavaScript, TypeScript (JSX/TSX) and Node.js (ES6, CommonJS). Basically what &lt;code&gt;skott&lt;/code&gt; does is deeply traversing a file directory, parsing all supported files (only JS(X)/TS(X) for now), resolving all module imports and build the whole graph from these and then expose a graph API from it.&lt;/p&gt;

&lt;p&gt;Having that graph generated, you can then use it in many ways, such as visualizing it using a web application. Here is an overview of the next version of the &lt;code&gt;skott&lt;/code&gt; &lt;a href="https://github.com/antoine-coulon/skott/issues/73" rel="noopener noreferrer"&gt;web application I'm currently revamping&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F43391199%2F254848997-312134ab-158c-4cc6-8c16-024dff28f7dd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F43391199%2F254848997-312134ab-158c-4cc6-8c16-024dff28f7dd.png" alt="skott web application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're familiar with static analysis tools (&lt;a href="https://dev.to/antoinecoulon/what-it-takes-to-build-a-static-analysis-tool-4p40"&gt;if not feel free to check my previous blog post on that subject&lt;/a&gt;), you might already be aware of some of the challenges we can face. In the context of &lt;code&gt;skott&lt;/code&gt;, here is a list of some of the challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;extracting&lt;/strong&gt; workspace information such as manifest files (package.json) or lockfiles (package-lock.json, yarn.lock, pnpm-lock.yml), configuration files (tsconfig.json, .eslintrc.json)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;traversing&lt;/strong&gt; the file system, using ignore patterns, discarding ".gitignore-d" files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;parsing&lt;/strong&gt; files using multiple parsers for each supported language (JS/TS) and managing to cover all specificities that can be enabled or not within that language (e.g: JSX/TSX for JavaScript/TypeScript)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;walking&lt;/strong&gt; the previously emitted AST to collect all the module declarations from each file AST, for both CommonJS and ESM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;resolving&lt;/strong&gt; all the module declarations to their real file system location, in order for us to incrementally resolve the entire graph&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;building&lt;/strong&gt; the graph by using all the resolved modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's quite a lot to do even though it's a simplified version. &lt;/p&gt;

&lt;p&gt;The question now is, how do you manage that complexity in a way that you're fast, confident and safe enough to add/remove/update features, fix bugs and process huge refactorings? Basically, how do you enable true software Agility?&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;The first thing that could come in mind are tests. Overall writing &lt;em&gt;good tests&lt;/em&gt; brings safety and helps to gain confidence. Nevertheless, there is a double-edged side with tests, because depending on how you manage to write them and essentially what is being tested i.e: the system under test (SUT) and how it is tested, it could lead to wrong confidence leading to unexpected bugs, sneakily introduce wrong design solutions such as coupling code to implementation details leading to painful and unsafe refactorings. As software engineers, this is something we should avoid at all cost.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In my previous blog post &lt;strong&gt;&lt;a href="https://dev.to/antoinecoulon/dont-target-100-coverage-387o"&gt;Don't target 100% coverage&lt;/a&gt;&lt;/strong&gt;, I quickly demonstrate how having tests written in a certain way can produce unexpected behaviors and false sense of security. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before continuing the post, I highly recommend to read ☝️ this blog post if you're not familiar with the different ways of bringing tests to the table I'm referring to, namely Test-First, Test-Last, Test-Driven Development, Mutation Testing, Code Coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test-First, Test-Last and Test-Driven Development
&lt;/h2&gt;

&lt;p&gt;Let's see the differences between these three.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test-First
&lt;/h3&gt;

&lt;p&gt;The process of writing the test before writing any line of code of the targeted feature. This aims to put a precise plan on what should be implemented but is indeed missing the most important part, the &lt;em&gt;how&lt;/em&gt; you manage to achieve the implementation, as Test-First is not providing an incremental feedback loop from which you can benefit a lot.&lt;/p&gt;

&lt;p&gt;Doing Test-First can be useful and can generally help increasing the covering of specifications with tests but kind of omits the fact that new constraints, hints and ways of doing things will be discovered along the way. Unfortunately, Test-First does not allow tests to drive the implementation, and favors the writing of big chunks of code at once, because you start from 0 lines of code to X lines of code where X is the number of lines of code that were needed for the test to pass. Amongst these chunks, some might be useless or take too much time to be introduced given the overall complexity that you have to deal with at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test-Last
&lt;/h3&gt;

&lt;p&gt;Probably the most popular way of testing, consists of writing all the tests after all the lines of the feature code have been written. This aims to bring safety around the already produced code but does it really help with that? Not really. One of the drawbacks of Test-Last is that it's often used as a way of converting a desire to gain confidence -by adding tests and increase the code coverage percentage- into a false sneaky feeling of safety. &lt;/p&gt;

&lt;p&gt;Remember the &lt;a href="https://en.wikipedia.org/wiki/Goodhart%27s_law" rel="noopener noreferrer"&gt;Goodhart's law&lt;/a&gt;: &lt;code&gt;When a measure becomes a target, it ceases to be a good measure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Consequently, a lot of unnecessary and noisy tests will be added along the way as they will try to cover parts of the code that don't essentially need to, either because they could have been covered by higher-level tests oriented by system state expectations or just that they test useless or repeated things already covered by other tests. How could you know if some test is useless or covers an behavior already covered by another test? You add a test, it passes, but is it due to the code you just added or was it the case before? 🧐&lt;/p&gt;

&lt;p&gt;Because Test-Last introduces tests in the very last step of the feature life cycle, it comes with a lot of pain points, such as revealing design smells or that implementation details were introduced or that the code is not easily testable. This is a waste of time, because you could have figured it out before. This will also most likely favor the introduction of mocks (&lt;a href="https://martinfowler.com/articles/mocksArentStubs.html" rel="noopener noreferrer"&gt;⚠️ I'm not referring to the test doubles in general, but especially a Mock which is one type of test double&lt;/a&gt;) that should be considered as a smell in most cases as it introduces a structural coupling between tests and the code (asserting that X will called Y with &lt;em&gt;abc&lt;/em&gt; parameters, this highly reduces flexibility and ability to refactor).&lt;/p&gt;

&lt;p&gt;In that case, tests become very fragile as they depend on implementation details and at the next function change they break, making you feel the anger towards the tests themselves.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Tests prevent me from refactoring as they break all the time when changing the code, I'm losing flexibility and it's not a convenient way of working" 😡  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Does it exist a better way of achieving efficiency and safety while keeping the code highly flexible, easily refactorable and abstracted from implementation details that we don't want to depend on feature-wise?&lt;/p&gt;

&lt;h3&gt;
  
  
  Test-Driven Development (TDD)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: My desire is not to advocate TDD at all cost, Test-Driven Development is not a silver bullet. The whole purpose is just to offer an overview about an highly misunderstood and under estimated discipline. It's then your choice to try it, blame it, blame me. But believe me, when you start being decent with TDD, you never look back.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test-Driven Development is a more evolved version of Test-First, it's essentially a software development discipline that drives you to find the quickest and best path of writing each line of code targeting a domain specification through fine-grained decomposed steps (so called baby steps), dealing with complexity incrementally.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It appears that Test-Driven Development relies on automated tests which its best companion to-date.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By using tests, TDD will drive the writing of code required to make a failing test ❌ pass to turn to GREEN ✅ in very short feedback loop cycles. &lt;/p&gt;

&lt;p&gt;If there is no prerequisite, that is no failing test, why would I add some lines of code? Nothing in my system justifies that need for adding lines of code yet. Maybe that the behavior is already implemented? The first step is to be sure that we have a failing test which is our first checkpoint to reach.&lt;/p&gt;

&lt;p&gt;Once we have that, we can start writing the code required to make the test pass. We are now sure that we did something useful, that is adding lines of code justified by the specification that turned from red (failing) to green (succeeding). &lt;/p&gt;

&lt;p&gt;Just after that, we have room for the third step of the TDD process which is the refactoring part during which you're free to refactor and produce the best code while keeping the test to green. That's why Test-Driven Development fundamentally helps designing software as it allows to infinitely and safely refactor (at any point in time) the code produced while ensuring that the expected behavior is still intact, which is one of the main benefits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test-Driven Development requires good Software Engineering skills to be effective&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TDD helps designing software because it creates a perpetual  confrontation between the design choices to be made at a time depending on the current system requirements. Nevertheless, TDD requires a good set of design and refactoring skills upfront in order for the discipline to be effective and increase productivity, otherwise you might end up being blocked at some point in the process or taking the wrong decisions (or even worse, no decisions at all).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test-Driven Development is all about feedback loop, so is the Software Engineering in general!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's put the TDD aside for a little bit and let's talk about some of the most common feedback loops we're all working with on a daily basis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Integrated Development Environment (IDE): all about feedback loop! How fast you want to know that the code is correctly written in the host language? The red underlines, warnings, auto-format, popups and whatever alerts can go through the whole IDE are part of the feedback loop, they help you know whether you are heading to the good path or not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compilers: all about feedback loop! Same here, the compiler provides you a way to know if the written code is semantically correct to some extent. Statically typed languages such as TypeScript are a great way of improving the feedback loops by adding type-safety + great developer experience with real-time integrated Language Server Protocol (LSP).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CI/CD Pipelines: all about the feedback loop, where you continuously want to know whether the product could be successfully deployed in production, passing all the verification steps. If that's not the case, you want to know it quickly to fix the problem right away.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these examples of feedback loops tend to leverage the &lt;strong&gt;&lt;code&gt;fail fast&lt;/code&gt; principle whose goal is to quickly identify failures, rather than letting them persist or be discovered later in a development process or worse letting the customers discover the bugs&lt;/strong&gt;. Feedback loop is the foundation of continuous improvement.&lt;/p&gt;

&lt;p&gt;Test-Driven Development is all about having the shortest feedback loop towards the written code, asserting whether the system is producing the expected outcome, when adding, removing or updating code. It appears that thanks to automated tests, the loop is quick enough in most cases as long as you respect the &lt;code&gt;Fast&lt;/code&gt; nature from the &lt;a href="https://medium.com/pragmatic-programmers/unit-tests-are-first-fast-isolated-repeatable-self-verifying-and-timely-a83e8070698e" rel="noopener noreferrer"&gt;F.I.R.S.T&lt;/a&gt; principles!&lt;/p&gt;

&lt;p&gt;One downside of TDD is that it can become counter-productive if not correctly applied. IMHO it is better not to practice TDD than to practice it the wrong way. Also, the learning curve is steep as it requires strong technical skills and mental shifts and a different way of approaching software development. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Putting that into small practical examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ok so now that we talked about Test-First, Test-Last and Test-Driven Development, let's come back with our initial question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you manage that complexity in a way that you're fast, confident and safe enough to add/remove/update features, fix bugs, continuously improve the code using refactoring?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For now, the only way I have been putting in practice that works for all these points, is Test-Driven Development. Of course you might be able to do the same without it, but at what cost, with what level of confidence and how fast? Without introducing any regression? I have been there too, now I could never do it again without it.&lt;/p&gt;

&lt;p&gt;Let's take an highly simplified version of a &lt;em&gt;skott&lt;/em&gt; feature.&lt;/p&gt;

&lt;p&gt;Given two JavaScript modules with one being a dependency of the other one, both should be analyzed and a graph should contain two vertices with one edge representing that relationship: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./feature.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Rest of the code consuming the function, we don't care about that&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;feature.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;read both files,&lt;/li&gt;
&lt;li&gt;find the module declarations and see that &lt;code&gt;index.js&lt;/code&gt; imports &lt;code&gt;feature.js&lt;/code&gt; module&lt;/li&gt;
&lt;li&gt;then build a graph of two vertices, &lt;code&gt;index.js&lt;/code&gt; and &lt;code&gt;feature.js&lt;/code&gt;, with a directed edge from &lt;code&gt;index.js&lt;/code&gt; to &lt;code&gt;feature.js&lt;/code&gt; representing the import dependency, we say that index.js is "adjacent to" feature.js.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consequently the expected outcome of our system is a graph shaped that way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"adjacentTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"feature.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"feature.js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"adjacentTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is our expected final shape, this is the expected outcome that we want &lt;code&gt;skott&lt;/code&gt; to produce.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test-First
&lt;/h3&gt;

&lt;p&gt;Practicing Test-First here would suggest us to start by writing the test first, with the whole expectation that we have from our system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Graph construction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When having two JavaScript modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When the first module imports the second&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should produce a graph with two nodes and one edge from the index module to the imported module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`
            export function add(a, b) {
                return a + b;
            }
          `&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`
            import { add } from './feature.js';
          `&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test itself includes the three main components, Arrange/Act/Assert. Running that test will make it fail for sure, as we don't have any code written yet. But now, how do you go from that test failing from passing?&lt;/p&gt;

&lt;p&gt;Little reminder of all the steps that we must go through to make the test pass:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file traversal&lt;/li&gt;
&lt;li&gt;file parsing &lt;/li&gt;
&lt;li&gt;module extraction &lt;/li&gt;
&lt;li&gt;module resolution &lt;/li&gt;
&lt;li&gt;graph construction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This represents such a long way and so many steps and components involved, hence we might spend a little a bit of time doing that. Unfortunately the test won't be useful during that whole time, it will only be there to assert the final result once we already produced all the required code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test-Last
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/baPIkfAo0Iv5K/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/baPIkfAo0Iv5K/giphy.gif" alt="nothing happens with Test-Last"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing happens there, Test-Last does not want us to write any test yet! Even though sometimes I hear it whispering something in my head:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/pDgHg2Lcju3Ty/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/pDgHg2Lcju3Ty/giphy.gif" alt="bon chance"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Test-Driven Development
&lt;/h3&gt;

&lt;p&gt;Ah finally, something that will help us achieving the desired behavior. As already said, Test-Driven Development wants us to take an incremental approach instead and let the code flow and grow in complexity in various steps. By design, it wants us to adopt a &lt;code&gt;baby step&lt;/code&gt; approach where we just add the minimum of code to make the test pass. &lt;/p&gt;

&lt;p&gt;In the context of TDD, here is the first test that crosses my mind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Graph construction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When not having any modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should produce an empty graph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First things first, we are focused on the shape of the contract we want to expose, it constraints the thinking around that, one problem at a time.&lt;/p&gt;

&lt;p&gt;One benefit of TDD is that the test become the first client of the code itself and you can let the design emerge progressively.&lt;/p&gt;

&lt;p&gt;Here is the quickest way of making the test pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Really easily, we just have to create a raw class with a method returning an hardcoded empty object.&lt;/p&gt;

&lt;p&gt;Then comes the wonder of &lt;strong&gt;what should be the next test&lt;/strong&gt;? Don't forget that the end goal is to be able to traverse files, and build module dependencies between each of them. &lt;/p&gt;

&lt;p&gt;TDD efficiency is enabled by the choice of the tests, there is no magic. The developer is responsible for thinking and finding the right path for the test order and each missed step or step that is too big will be a missed opportunity to entirely benefit from TDD. But don't worry if you end up in that situation and think of an intermediate step that can be valuable, you can still downshift gear.&lt;/p&gt;

&lt;p&gt;So what test comes next? After validating the case where we don't have any module, what about introducing the case where we finally have one module to analyze?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When having one JavaScript module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should produce a graph with the module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/**
       * ARRANGE
       * 
       * How can we create a file system context including that file specifically
       * for this test?
       */&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if this appears to be simple, it grows in complexity as  we introduce the notion of file in the Arrange part of the test. Our tool will be based on the file system, meaning that it will need to read from it at some point. Does it mean that the test itself should read from the file system? Not at all. &lt;/p&gt;

&lt;p&gt;But why not using the real filesystem right away, using the Node.js API for instance? &lt;/p&gt;

&lt;p&gt;Introducing the use of the real file-system is something we want to avoid with unit tests, &lt;strong&gt;we want them to be fast, isolated and repeatable across executions&lt;/strong&gt;. This is not the case as the filesystem layer would not check any of these criteria. We want to have a fully manageable and specialized version of the Node.js filesystem module. Do we need to fake everything within it? Not at all, we just need to cover the subset of it we need for now.&lt;/p&gt;

&lt;p&gt;As you can see, that small test brings a whole new level of thinking around the new feature. You could be thinking that it's something we could avoid if we were not writing any test and that it complicates our task for not much. But indeed, this forces us to think about building a testable code on which we have full control over and that already brings us to designing solutions.&lt;/p&gt;

&lt;p&gt;What we want first is to create a minimalistic and in-memory file system that can fake a real one.&lt;/p&gt;

&lt;p&gt;Let's go back at our second test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should produce a graph with the module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FakeFileSystem&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have that in-memory context created, we want it to be able to be used within that Graph Resolver context, in other words we want it to be &lt;strong&gt;injected&lt;/strong&gt; in the Graph Resolver context.&lt;/p&gt;

&lt;p&gt;This naturally leads us to &lt;strong&gt;Dependency Injection&lt;/strong&gt; (DI).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dependency injection is a pattern for decoupling the usage of dependencies from their actual creation process. In other words, it is a process of injecting dependencies of a service from the outside world. The service itself doesn't know how to create its dependencies.&lt;/p&gt;

&lt;p&gt;The dependency there (the FileSystem module) is created outside of the &lt;code&gt;GraphResolver&lt;/code&gt; context and can be injected right away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeFileSystem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FakeFileSystem&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FakeFileSystem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Dependency Injection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graphResolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the dependency injection in place, we can fake the required behavior from the file system module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeFileSystem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;readFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple fake filesystem that emulates the operations we need for now, that is &lt;code&gt;createFile&lt;/code&gt; and &lt;code&gt;readFiles&lt;/code&gt;. Nothing much! We don't need to cover everything, only what's needed in the context of the test.&lt;/p&gt;

&lt;p&gt;To make the test pass, we can now consume that newly available implementation in the &lt;code&gt;GraphResolver&lt;/code&gt; service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FakeFileSystem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFiles&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now that passes, just matching our expectations. Note how much we're only focused on the current desired behavior, we just produced the only minimum of code we needed. For instance the &lt;code&gt;readFiles&lt;/code&gt; method only yields the filename as the test only requires us to have a record including that name, even though we know for sure that later one we'll also need the file content.&lt;/p&gt;

&lt;p&gt;Also, we're fully in-memory and isolated from the real filesystem, the dependency only produce the outcome we need, we don't need to fake the whole filesystem API of Node.js or whatever runtime we are using! &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that in this case I'm voluntary skipping some of the steps in between as I know for sure that we want to read all files from the directory at some point. Instead of just introducing &lt;code&gt;readFile&lt;/code&gt; method (as we only have one file), I go straight into what would have been the next step (&lt;code&gt;readFiles&lt;/code&gt;). As a rule of thumb, it's generally a good practice to follow a sequence of Transformations that you should go through to produce the minimal code in order for the test to pass in short loops. If you're interesting in knowing more about that, you can read &lt;a href="https://blog.cleancoder.com/uncle-bob/2013/05/27/TheTransformationPriorityPremise.html" rel="noopener noreferrer"&gt;the Transformation Priority Premise from Robert C. Martin&lt;/a&gt;  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the green step, TDD comes in with a third phase which is the refactoring. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Refactoring is about changing the system design without altering that is changing/adding/removing parts of the system behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Luckily, we can use that phase to improve a tiny thing that you might have noticed in the Dependency Injection system we currently have.&lt;/p&gt;

&lt;p&gt;Dependency Injection can be used in a way that decouples from concrete implementations of the dependencies. In the case above, &lt;code&gt;GraphResolver&lt;/code&gt; is directly coupled to a &lt;code&gt;FakeFileSystem&lt;/code&gt; instance letting no room for flexibility and offers no way of injecting anything else that would match the same contract. More importantly, it leaks implementation details and things that should not be part of the contract. In our case, &lt;code&gt;FakeFileSystem&lt;/code&gt; exposes a &lt;code&gt;createFile&lt;/code&gt; method that exists just for the purpose of the test but has no value in the context of the &lt;code&gt;GraphResolver&lt;/code&gt;, so &lt;code&gt;GraphResolver&lt;/code&gt; should not know about that method but only what it cares about, that is the &lt;code&gt;readFiles&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Consequently, we can improve that by introducing a generic interface in a way that &lt;code&gt;GraphResolver&lt;/code&gt; now depends on abstractions and not on concrete implementation. This brings us to the &lt;strong&gt;Dependency Inversion Principle&lt;/strong&gt; (the &lt;strong&gt;D&lt;/strong&gt; from SOLID principles). That way, &lt;code&gt;skott&lt;/code&gt; could have an runtime-agnostic way of traversing file systems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FileSystem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;readFiles&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeFileSystem&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;FileSystem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// unchanged&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FileSystem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                                           &lt;span class="c1"&gt;// ^ this changes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All tests are still passing because we just played a bit more with interfaces and with the static compiler to narrow the correct types into the &lt;code&gt;GraphResolver&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No more refactoring? Let's get closer to the objective.&lt;/p&gt;

&lt;p&gt;Now we want to start introducing the concept of dependencies between the JavaScript modules. As already said, dependencies between JS modules can be modeled using a &lt;a href="https://dev.to/antoinecoulon/master-directed-graphs-by-example-with-javascript-4oef"&gt;Directed Graph&lt;/a&gt;, in which files would be represented as &lt;em&gt;vertices&lt;/em&gt; and relationships between files would be represented as directed &lt;em&gt;edges&lt;/em&gt; between &lt;em&gt;vertices&lt;/em&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using the Graph terminology, a &lt;em&gt;vertex A&lt;/em&gt; that depends on another &lt;em&gt;vertex B&lt;/em&gt; is said to be "adjacent to B".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once again, we introduce one concept at a time. This time, we don't really need to add a new test, we can just update the last one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;expect(graph.resolve()).toEqual({
&lt;/span&gt;&lt;span class="gd"&gt;-        "index.js": {},
&lt;/span&gt;&lt;span class="gi"&gt;+        "index.js": {
+          adjacentTo: [],
+        },
&lt;/span&gt;      });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fails, now we make it pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;resolve() {
&lt;/span&gt;    for (const filename of this.fileSystem.readFiles()) {
&lt;span class="gd"&gt;-      this.graph[filename] = {};
&lt;/span&gt;&lt;span class="gi"&gt;+      this.graph[filename] = {
+        adjacentTo: [],
+      };
&lt;/span&gt;    }
&lt;span class="err"&gt;
&lt;/span&gt;    return this.graph;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;// What is the next step? Adding two modules would not allow us to go forward. We need to find a test that will drive us to the cases where we introduce the concept of import that will create an edge between two modules.&lt;/p&gt;

&lt;p&gt;Let's add a test that will force us to start dealing with dependencies between modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When having two modules with one dependency&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should produce a graph with both modules and a  dependency between the two&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileSystem&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileWithModuleImport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`import "./feature.js";`&lt;/span&gt;

      &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileWithModuleImport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GraphResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still with the idea of writing the easiest code possible to make the test pass, we can add a little &lt;code&gt;if statement&lt;/code&gt; in the &lt;code&gt;resolve&lt;/code&gt; method of the &lt;code&gt;GraphResolver&lt;/code&gt;. From the &lt;code&gt;Transformation Priority Premise&lt;/code&gt;, this is ordered as the middle of the transformation we should process &lt;code&gt;(unconditional-&amp;gt;if) splitting the execution path&lt;/code&gt;, splitting the execution path to match expectations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  resolve() {
    for (const [fileName, fileContent] of this.fileSystem.readFiles()) {
&lt;span class="gi"&gt;+      if (fileContent.includes("import")) {
+        const moduleName = fileContent.split("./")[1].split("'")[0];
+
+        this.graph[fileName] = {
+         adjacentTo: [moduleName],
+        };
+
+       continue;
+     }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      this.graph[fileName] = {
        adjacentTo: [],
      };
    }
&lt;span class="err"&gt;
&lt;/span&gt;    return this.graph;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test is passing ✅ &lt;/p&gt;

&lt;p&gt;Note how ugly that "split" is, actually it might be the ugliest statement I have even written but we don't care, it fits our needs: it turns the test to green.&lt;/p&gt;

&lt;p&gt;Remember after this step, you're free the refactor as much as you want, so no stress, we'll be able to do better just after.&lt;/p&gt;

&lt;p&gt;Let's try to slightly improve the code during the refactoring step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;resolve() {
&lt;/span&gt;    for (const [fileName, fileContent] of this.fileSystem.readFiles()) {
      if (fileContent.includes("import")) {
&lt;span class="gi"&gt;+        const moduleImportParser = /import '(.*)';/g;
+        const [moduleImport] = +moduleImportParser.exec(fileContent);
&lt;/span&gt;         // path comes from Node.js "path" module
&lt;span class="gi"&gt;+        const moduleName = path.basename(moduleImport);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;        this.graph[fileName] = {
          adjacentTo: [moduleName],
        };
&lt;span class="err"&gt;
&lt;/span&gt;        continue;
      }
&lt;span class="err"&gt;
&lt;/span&gt;      this.graph[fileName] = {
        adjacentTo: [],
      };
    }
&lt;span class="err"&gt;
&lt;/span&gt;    return this.graph;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The way parsing is done in the two previous steps is an implementation detail there and is part of our own business logic, it should remain hidden allowing heavy refactoring without breaking the API surface. As long as the &lt;code&gt;resolve&lt;/code&gt; method returns the expected graph we are fine because it's what matters the most.&lt;/p&gt;

&lt;p&gt;While progressively adding cases where you want to reliably parse all types of module imports (and there is a bunch, including fun edge cases), you will realize that using a RegExp does not scale well and becomes to complex to manage. But it's fine, nothing prevents you there from introducing an ECMAScript-compliant parser, such as &lt;code&gt;meriyah&lt;/code&gt;, &lt;code&gt;acorn&lt;/code&gt;, &lt;code&gt;swc&lt;/code&gt;, that does the job for you! The best part is that this would still remain hidden inside the internals of your use case, allowing infinite refactoring and improvements.&lt;/p&gt;

&lt;p&gt;I'm not going to into the implementation of the parser itself (you can check &lt;a href="https://github.com/antoine-coulon/skott/blob/main/packages/skott/src/modules/walkers/ecmascript/javascript/walker.ts" rel="noopener noreferrer"&gt;&lt;code&gt;skott&lt;/code&gt; source code to see a complete example&lt;/a&gt;), but hopefully you start being able to imagine what are the next steps of this use case. I also wish that you were able to see the advantages that Test-Driven Development brings in that little context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrap up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By dealing with few use cases, we were able to see how Test-Driven Development incrementally drives us to the development of a feature. &lt;/p&gt;

&lt;p&gt;Not only this helps us facing constraints very early in the process thanks to the feedback loop, it also naturally forces us to introduce a way to make the code easily testable, fast, isolated, repeatable, via Dependency Injection. This also reduces the level of complexity we have to deal with at each new step and test added, while going way faster and safer.&lt;/p&gt;

&lt;p&gt;Moreover, we can also abuse from heavy refactoring phases to make the code cleaner (even though clean code and great design are prerequisites to process efficient refactoring) while being sure that the system behavior still matches our expectations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Personal take:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One thing I know for sure is that I would not feel confident with &lt;code&gt;skott&lt;/code&gt; features if I hadn't brought 130+ unit tests with Test-Driven Development. Features can be added without any fear, refactoring can be done with ease and confidence. &lt;/p&gt;

&lt;p&gt;Nevertheless this does not mean that &lt;code&gt;skott&lt;/code&gt; can not produce bugs, it simply means that for the use cases where &lt;code&gt;skott&lt;/code&gt; is expected to work, it will most likely work as expected. In other words, a &lt;code&gt;bug&lt;/code&gt; will most of the time be due to a missing system behavior description or edge case. And it's totally fine, because to fix that, you only have to add that test case reproducing the missing behavior, and let the TDD flow!&lt;/p&gt;

&lt;p&gt;Link to skott's GitHub repository: &lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;https://github.com/antoine-coulon/skott&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you at the next episode 👋🏻&lt;/p&gt;

</description>
      <category>tdd</category>
      <category>typescript</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Don't target 100% coverage</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Thu, 19 Jan 2023 08:39:04 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/dont-target-100-coverage-387o</link>
      <guid>https://dev.to/antoinecoulon/dont-target-100-coverage-387o</guid>
      <description>&lt;h2&gt;
  
  
  Don't target 100% coverage... but achieve it anyway!
&lt;/h2&gt;

&lt;p&gt;I recently noticed that a lot of people were advising to reach 100% of code coverage and were presenting it as a primary indicator of code quality. While I agree on the fact that it's an interesting metric, everyone must be aware that having 100% of coverage &lt;strong&gt;does not indicate the quality of tests&lt;/strong&gt; and &lt;strong&gt;the way to achieve a good coverage&lt;/strong&gt; is more important that being able to produce 100% of coverage as an end, it &lt;strong&gt;must not be a target in the first place&lt;/strong&gt;. Let's see why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Coverage
&lt;/h2&gt;

&lt;p&gt;First things first let's briefly explain what is &lt;strong&gt;code coverage&lt;/strong&gt; for those who are not really familiar with the concept. &lt;/p&gt;

&lt;p&gt;Code coverage is a technique aiming to determine what parts of your program is being covered by a test. Basically, the code coverage tool instantiates counters and they are incremented once a line of your program is traversed by one of your tests.&lt;/p&gt;

&lt;p&gt;Here is a quote from &lt;a href="https://istanbul.js.org/" rel="noopener noreferrer"&gt;istanbul, one of the most used code coverage tool&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Istanbul instruments your ES5 and ES2015+ JavaScript code with line counters, so that you can track how well your unit-tests exercise your codebase.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Code coverage tools often collect various metrics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;% of statements traversed &lt;/li&gt;
&lt;li&gt;% of branches traversed &lt;/li&gt;
&lt;li&gt;% of functions traversed&lt;/li&gt;
&lt;li&gt;% of lines traversed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, code coverage tools &lt;em&gt;just&lt;/em&gt; collect the amount of production code traversed by a test, whether the assertion is relevant or not, which is where the devil resides.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap 👹
&lt;/h2&gt;

&lt;p&gt;Let's see a trap example with a simple project using &lt;code&gt;c8&lt;/code&gt; as our code coverage tool (&lt;code&gt;istanbul&lt;/code&gt; unfortunately does not natively work with ESM). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can grab the &lt;a href="https://github.com/antoine-coulon/dont-target-100-coverage" rel="noopener noreferrer"&gt;source code here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;index.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now here is one simple test that we would expect to be truthy:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.spec.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When running the test, we can see that it turns to &lt;em&gt;green&lt;/em&gt; and the coverage is automatically generated for us:&lt;/p&gt;

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

&lt;p&gt;As we can see, everything is 100% covered by tests, all the statements, all the functions, all the branches, etc. So everything is fine, so we could just ship the feature right? 🧐&lt;/p&gt;

&lt;p&gt;But what if someday someone just changes a little detail, pretty confident that it will work as the &lt;strong&gt;unit test still succeeds after the change&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks to &lt;a href="https://www.linkedin.com/in/%E2%9A%A1%EF%B8%8Fmicha%C3%ABl-azerhad-9058a044/" rel="noopener noreferrer"&gt;Michaël Azerhad&lt;/a&gt; who provided me that example while back!&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;export function add(a, b) {
&lt;/span&gt;&lt;span class="gi"&gt;+  return a - b;
&lt;/span&gt;&lt;span class="gd"&gt;-  return a + b;
&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test suite still passes and coverage is still showing us 100% of coverage.&lt;/p&gt;

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

&lt;p&gt;As simple as the example may seem, it demonstrates that coverage only checks that a given chunk of code is at least traversed once, but it will never tell you if a test is valuable nor useful in some kind of way. Consequently there is a danger which is that it can make you feel (wrongly) safe about your code, just based on the fact that they are traversed by a test at some point in time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't let "100% coverage" be your primary concern 🖐
&lt;/h2&gt;

&lt;p&gt;Don't get me wrong, I'm not saying that coverage is not useful in itself.  &lt;/p&gt;

&lt;p&gt;However aiming towards reaching 100% after having written the code will most likely produce false positive tests in a "Test-Last" fashion, just to make statistics look better. I would even say that Code Coverage Driven Development (CCDD) sometimes inherits from the disadvantages of the "Test-Last" approach, that is most of the time either writing irrelevant or even worse useless tests. They both make you feel safe and confident as the console &lt;em&gt;only shows green lights&lt;/em&gt;, but it won't avoid you to get in trouble.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutation testing, an under-estimated tool 🔍
&lt;/h2&gt;

&lt;p&gt;Because I'm lazy, I'll once again simply quote the definition &lt;a href="https://stryker-mutator.io/docs" rel="noopener noreferrer"&gt;from the Stryker documentation&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bugs, or mutants, are automatically inserted into your production code. Your tests are run for each mutant. If your tests fail then the mutant is killed. If your tests passed, the mutant survived. The higher the percentage of mutants killed, the more effective your tests are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, it does something fundamentally different from code coverage which is adding variants to branches of your program. By mutating your code, the mutation testing tool can detect whether your test suite was correctly covering or not the behavior produced by the mutation.&lt;/p&gt;

&lt;p&gt;Let's try it on our small example using &lt;a href="https://stryker-mutator.io/" rel="noopener noreferrer"&gt;Stryker&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run mutation-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the test produces the following output:&lt;/p&gt;

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

&lt;p&gt;As you can see, &lt;strong&gt;one mutant survived&lt;/strong&gt; which is the one changing the &lt;em&gt;Arithmetic Operator&lt;/em&gt; which is exactly what we were looking for. Consequently, we can see that it does a step forward by allowing tests to be tested via the mutation of the production code itself. The more your tests kill mutants, the more they &lt;em&gt;tend&lt;/em&gt; to be relevant. You could have 100% of coverage by having only 20 to 30% of the mutants killed, which is a big smell. Be careful with code coverage!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might have guessed it, but mutation testing tools can also provide &lt;em&gt;code coverage&lt;/em&gt; reports. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Automatically reaching 100% coverage and 100% of mutants killed using Test-Driven Development 🤹‍♀️
&lt;/h2&gt;

&lt;p&gt;With a simple example we just showed that coverage as such is not enough to ensure tests quality. We also saw that mutation testing helps finding incomplete/missing test suites. What if I told you that a good coverage and a high rate of mutants killed can be a positive side effect of a discipline that does not care about any coverage in the first place?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test-Driven Development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article is not dedicated to Test-Driven Development (TDD) is, but I'll try to make a short introduction.&lt;/p&gt;

&lt;p&gt;Test-Driven Development is a software development discipline that aims to drive the writing of code required to make a failing test ❌ (prerequisite) pass i.e. turn to GREEN ✅ in very short feedback loop cycles. Test-Driven Development fundamentally helps designing software as it allows to infinitely and safely refactor (at any point in time) the code produced, which is one of the main benefits.&lt;/p&gt;

&lt;p&gt;When doing TDD correctly, because each line of code produced must be justified by a failing test in the first place, 100% coverage will be naturally achieved. All the mutants will also be killed if TDD was done correctly, as changing a line of code or one statement will without a doubt break one test, otherwise it means that some code was shamefully produced without having a failing test first!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not only you'll achieve 100% of code coverage and 100% of killed mutants, but you'll be able to produce a code with a higher level of quality, depending on your refactor skills (because TDD can drive you writing a well designed code but the skillset required to do so has nothing to do with TDD).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course Test-Driven Development is not a silver bullet, but it is to date the best way of mixing both code quality with code coverage and the best part is that you'll most likely do it unconsciously.&lt;/p&gt;

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

&lt;p&gt;Don't get me wrong, code coverage is useful in many ways, as it can show how well a codebase is covered by tests at a very high level, for instance having a very low percentage of coverage means that there is not enough tests, so it's good metric to start with. &lt;/p&gt;

&lt;p&gt;Nevertheless, having the opposite which is a high percentage of code coverage does not mean that tests are relevant. The best way of achieving both a high percentage of coverage and tests reliability is by not targeting it in the first place, otherwise it might lead people to write more or less (often less) relevant tests just make the percentage a little bit better, the worst part being that it makes everyone feel safer.&lt;/p&gt;

&lt;p&gt;By also using mutation testing tools and by mastering disciplines such as Test-Driven Development (or at least Test-First), you can end up achieving &lt;strong&gt;100% coverage and 100% mutants killed&lt;/strong&gt; by &lt;strong&gt;not caring at all (0%)%&lt;/strong&gt; of all these metrics. That, is the target.&lt;/p&gt;

&lt;p&gt;Project sample can be found there: &lt;a href="https://github.com/antoine-coulon/dont-target-100-coverage" rel="noopener noreferrer"&gt;https://github.com/antoine-coulon/dont-target-100-coverage&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Exploring the latest features of Skott: road to V1</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Mon, 16 Jan 2023 09:30:00 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/exploring-the-latest-features-of-skott-road-to-v1-21i</link>
      <guid>https://dev.to/antoinecoulon/exploring-the-latest-features-of-skott-road-to-v1-21i</guid>
      <description>&lt;p&gt;Hello everyone, I hope you're doing well.&lt;/p&gt;

&lt;p&gt;Following the series of &lt;code&gt;My journey of building Skott, an open-source Node.js library&lt;/code&gt;, I wanted to share with you a quick update about the latest features added. Implementing these features will be the opportunity for me to share with you what it took to build them.&lt;/p&gt;

&lt;p&gt;For those who are not familiar with the tool, I suggest you to quickly check &lt;a href="https://dev.to/antoinecoulon/introducing-skott-the-new-madge-1bfl"&gt;my first article introducing Skott&lt;/a&gt; 😎&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web application (v0.11.0)&lt;/strong&gt; 👩‍💻&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental comparison (v0.12.0)&lt;/strong&gt; 🕰&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detecting unused third-party dependencies (v0.13.0)&lt;/strong&gt; 🔍&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Web application (introduced in v0.11.0)
&lt;/h2&gt;

&lt;p&gt;For its first versions, Skott's visualization was only rendered in our beloved &lt;em&gt;command line interface&lt;/em&gt; which is cool but not really ideal to represent real-world graph structures.&lt;/p&gt;

&lt;p&gt;Since &lt;strong&gt;v0.11.0&lt;/strong&gt;, Skott now embeds the new &lt;strong&gt;"webapp"&lt;/strong&gt; &lt;em&gt;display mode&lt;/em&gt;, which is the new default display mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;skott &lt;span class="nt"&gt;--displayMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;webapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you want &lt;code&gt;third-party dependencies&lt;/code&gt; (npm) and &lt;code&gt;built-in dependencies&lt;/code&gt; (Node.js) to be rendered in the app, don't forget to provide the flags to the CLI, otherwise they won't be collected nor displayed:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;skott &lt;span class="nt"&gt;--displayMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;webapp &lt;span class="nt"&gt;--trackThirdPartyDependencies&lt;/span&gt; &lt;span class="nt"&gt;--trackBuiltinDependencies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the graph generated, the web application is automatically opened in your default browser on a free port:&lt;/p&gt;

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

&lt;p&gt;The application renders a 2D network in which files are represented by the nodes of the network and the links between these files are represented by directed edges.&lt;/p&gt;

&lt;p&gt;Within the left sidebar, few things are displayed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some statistics are exposed (number of files, circular dependencies, etc)&lt;/li&gt;
&lt;li&gt;Some visualization options can be toggled on/off to highlight or make appear additional nodes/edges representing &lt;code&gt;third-party&lt;/code&gt; or &lt;code&gt;built-in&lt;/code&gt; dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make the search of specific files easier when processing large graphs, a global search &lt;a href="https://github.com/antoine-coulon/skott/commit/403e3156a099ebd53a5e3207a380d6a7801b7915" rel="noopener noreferrer"&gt;was recently introduced&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;By using the &lt;code&gt;CMD+K&lt;/code&gt;/&lt;code&gt;CTRL+K&lt;/code&gt; command, files can be searched and then focused on:&lt;/p&gt;

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

&lt;p&gt;Thanks to &lt;a href="https://github.com/bam-charlesbo" rel="noopener noreferrer"&gt;@bam-charlesbo&lt;/a&gt; for &lt;a href="https://github.com/antoine-coulon/skott/issues/18" rel="noopener noreferrer"&gt;suggesting new features&lt;/a&gt; about the webapp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incremental graph processing (introduced in v0.12.0)
&lt;/h2&gt;

&lt;p&gt;Computing graph is expensive as it includes a static analysis on each file of the project mainly &lt;a href="https://dev.to/antoinecoulon/what-it-takes-to-build-a-static-analysis-tool-4p40"&gt;involving parsing and AST walking&lt;/a&gt;. Depending on the language, some parsers are faster than others, for instance JavaScript parsers are naturally faster than TypeScript ones as TypeScript embeds a lot more of information encoded in the language at the type-level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing an incremental comparison&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even if Skott can analyze thousands of files in a matter of few seconds, performance always matter for a better DX. So here is a first step in order to make it nicer!&lt;/p&gt;

&lt;p&gt;In the past, &lt;a href="https://dev.to/antoinecoulon/lets-implement-the-incrementalaffected-pattern-the-killer-feature-used-by-turborepo-nx-rush-and-many-more-popular-tools-4l51"&gt;I reproduced a very minimalist implementation of the &lt;strong&gt;Affected/Incremental&lt;/strong&gt; pattern&lt;/a&gt; that most of the monorepo tools embed natively allowing to gain a lot of performance on heavy project graphs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're interested in knowing more about how it works under the hood, you can directly refer to this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because most of the time project graphs do not entirely change (think of few files changes per commit), using &lt;strong&gt;incremental comparison&lt;/strong&gt; would allow us to heavily benefit from caching.&lt;/p&gt;

&lt;p&gt;From &lt;strong&gt;v0.12.0&lt;/strong&gt;, a first version of an &lt;strong&gt;incremental comparison&lt;/strong&gt; can done by providing the &lt;code&gt;--incremental&lt;/code&gt; argument from the CLI. A folder ".skott" will be generated at the same location the command was run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;skott &lt;span class="nt"&gt;--incremental&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After re-running the same command, you should be able to see the difference between the time took for the analysis without the cache. Note that difference may seem slim if the project doesn't contain much files.&lt;/p&gt;

&lt;p&gt;Note that because resolving paths from a cache involves many edge cases, &lt;code&gt;incremental&lt;/code&gt; mode is &lt;em&gt;not turned on by default yet&lt;/em&gt;, but will be once the feature covers most of the cases I have in mind. Most of the time using it works well though, so don't hesitate to abuse the feature and open issues if ever you encounter some problems while enabling the incremental feature to make it more stable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unused dependencies (introduced in v0.13.0)
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;unused dependencies&lt;/em&gt; feature will try to cover as much use cases as possible over time. For now what have been introduced in &lt;a href="https://github.com/antoine-coulon/skott/commit/69d2dd781765bb26d6d25a26dce6431f6b1161ff" rel="noopener noreferrer"&gt;v0.13.0&lt;/a&gt; is the detection of &lt;strong&gt;unused npm production dependencies&lt;/strong&gt;. Note that only production code is analyzed as of now, so if you're using &lt;em&gt;production&lt;/em&gt; dependencies in test files, Skott will report them as unused.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;skott &lt;span class="nt"&gt;--showUnusedDependencies&lt;/span&gt; &lt;span class="nt"&gt;--trackThirdPartyDependencies&lt;/span&gt; &lt;span class="nt"&gt;--displayMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;raw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;--trackThirdPartyDependencies&lt;/code&gt; is required for unused npm dependencies to be found.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Thanks to &lt;a href="https://github.com/ild0tt0re" rel="noopener noreferrer"&gt;@ild0tt0re&lt;/a&gt; for that &lt;a href="https://github.com/antoine-coulon/skott/issues/14" rel="noopener noreferrer"&gt;feature request&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;That's it for the latest updates about Skott :) As promised in the latest blog posts of this series, the next one will be about how I leveraged &lt;strong&gt;Test-Driven Development and Dependency Injection&lt;/strong&gt; to &lt;em&gt;confidently develop&lt;/em&gt; the whole chain of file exploring, parsing and analysis 😎 &lt;/p&gt;

&lt;p&gt;Also, &lt;a href="https://github.com/antoine-coulon" rel="noopener noreferrer"&gt;here is the repository&lt;/a&gt; if you want to know more.&lt;/p&gt;

&lt;p&gt;Thanks for reading, see you next time!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>node</category>
    </item>
    <item>
      <title>📦 Everything you need to know: package managers</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Fri, 18 Nov 2022 09:51:21 +0000</pubDate>
      <link>https://dev.to/nodesecure/everything-you-need-to-know-package-managers-286c</link>
      <guid>https://dev.to/nodesecure/everything-you-need-to-know-package-managers-286c</guid>
      <description>&lt;p&gt;Welcome everyone! This article is the first one of the &lt;strong&gt;Everything you need to know&lt;/strong&gt;, a Software Engineering series.&lt;/p&gt;

&lt;p&gt;In this series, I will try to give you a solid basic understanding about Software Engineering concepts I consider important.&lt;/p&gt;

&lt;p&gt;All modern computer systems include tools that automate the process of installing, uninstalling and updating software.&lt;/p&gt;

&lt;p&gt;This responsibility is that of a package manager and several can intervene within the same computer system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operating system
&lt;/h3&gt;

&lt;p&gt;The majority of Unix-based operating systems embed a package manager as standard, providing a multitude of different packages very simply.&lt;/p&gt;

&lt;p&gt;If you have ever used a Linux distribution such as Ubuntu or Debian, you've probably used a package manager before. If I say &lt;code&gt;apt-get update&lt;/code&gt; does that ring a bell?&lt;/p&gt;

&lt;p&gt;This command tells APT to update all versions of packages installed. &lt;a href="https://en.wikipedia.org/wiki/APT_(software)" rel="noopener noreferrer"&gt;APT (Advanced Packaging Tool)&lt;/a&gt; is a package manager embedded very widely as standard on Linux operating systems. To install a package, you can for example enter the command &lt;code&gt;apt-get install &amp;lt;package&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Programming language
&lt;/h3&gt;

&lt;p&gt;Most programming languages ​​can embed their own with package managers, either natively or provided within their respective ecosystem.&lt;/p&gt;

&lt;p&gt;Take for example &lt;a href="https://www.npmjs.com" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, the default package manager for Node.js. We can also mention &lt;a href="https://pip.pypa.io/en/stable/getting-started/" rel="noopener noreferrer"&gt;pip&lt;/a&gt; for Python, &lt;a href="https://www.nuget.org/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt; for C#, &lt;a href="https://getcomposer.org/" rel="noopener noreferrer"&gt;Composer&lt;/a&gt; for PHP, etc. Similar to APT, npm makes it easy to install packages using the &lt;code&gt;npm install &amp;lt;package&amp;gt;&lt;/code&gt; command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For this article, I decided to take npm as an example.&lt;br&gt;
npm is indeed a very good support to highlight the advantages but also the disadvantages that a package manager can have.&lt;br&gt;
The advantages and disadvantages listed in the following part are valid for all package managers.&lt;/p&gt;

&lt;p&gt;npm is installed alongside Node.js. To reproduce these examples, [you only need to install Node.js here].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In four parts, we will see what are the main reasons for such an expansion of package managers to all layers of a computer system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.  Ease of use and maintenance of packages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The main interest of a package manager is obviously to simplify the installation of dependencies external to our application. Before the rise of npm in January 2010, the dependencies of a JavaScript application were mostly installed manually. By "manual installation" I mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;downloading a zip archive from a remote server&lt;/li&gt;
&lt;li&gt;unzipping the archive in the project&lt;/li&gt;
&lt;li&gt;manual referencing of the installed version, and this with each update of a dependency. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a package manager like npm, we therefore benefit from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplified installation of a package &lt;code&gt;npm install &amp;lt;package&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The simplified update of a package &lt;code&gt;npm update &amp;lt;package&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The simplified removal of a package &lt;code&gt;npm uninstall &amp;lt;package&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The packages are installed in a &lt;strong&gt;node_modules&lt;/strong&gt; folder adjacent to the application and which is entirely managed by npm. All packages located in the node_modules folder can be directly imported from the application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In general, each programming language natively embeds its own module resolution management mechanism.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;1.1. Install&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order for a package to be installed, we first need a name which is in most cases used as a unique identifier. Naming conventions can differ from one ecosystem to another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;rxjs 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this command, the package manager will search within the registry for a package that has the name &lt;strong&gt;rxjs&lt;/strong&gt;. When the version is not specified, the package manager will usually install the latest available version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2. Use&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ECMAScript Modules (ESM)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// CommonJS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module systems integrated into the programming languages ​​make it possible to import a library installed locally and sometimes remotely (like Go or Deno for example). In this case with Node.js, the package must be installed locally in a node_modules folder. With Node.js, &lt;a href="https://nodejs.org/api/modules.html#all-together" rel="noopener noreferrer"&gt;the module resolution algorithm&lt;/a&gt; allows the dependency to be in a node_modules folder either adjacent to the source code or in a parent folder (which sometimes leads to an unexpected behavior).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Managing the consistency of installed packages
&lt;/h3&gt;

&lt;p&gt;Now, let's dive into a little more detail on one very important aspect that a package manager must manage: &lt;strong&gt;state consistency between installed packages&lt;/strong&gt;. So far, installing a package looks like a trivial task, which is just to automate downloading a package of a certain version and making it available in a conventional folder that the application has access to.&lt;/p&gt;

&lt;p&gt;However this management of consistency between packages turns out to be relatively difficult and the way of modeling the dependency tree varies according to the ecosystems. Most of the time, we talk about a dependency tree, but we can also talk about a dependency graph, in particular a directed graph.&lt;/p&gt;

&lt;p&gt;If you are not familiar with the concept of directed graphs, I invite you &lt;a href="https://dev.to/antoinecoulon/master-directed-graphs-by-example-with-javascript-4oef"&gt;to read the series of articles I wrote about it on dev.to with examples in JavaScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The implementations of these data structures can be drastically different depending on the ecosystem of a package manager, but also between package managers of the same ecosystem (npm, yarn, pnpm for Node.js for example).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How to ensure that all developers share the same dependencies and therefore the same versions of each underlying library?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Still in the context of npm, let's take for example a very simple list of dependencies, expressed as an object in the &lt;em&gt;package.json&lt;/em&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;package.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"myDependencyA"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;0.1.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This object describes a dependency of our project on the &lt;em&gt;myDependencyA&lt;/em&gt; library downloadable from the npm registry. &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;Semantic Versioning&lt;/a&gt; here constrains the version of the library to be installed (here lower than 0.1.0). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Semantic version management (commonly known as SemVer) is the application of a very precise specification to characterize the version of software. For more information on this subject, I invite you to take a look at the official specification &lt;a href="https://semver.org/lang/fr/" rel="noopener noreferrer"&gt;https://semver.org/lang/fr/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our case, by remaining on the classic &lt;code&gt;&amp;lt;major&amp;gt;.&amp;lt;minor&amp;gt;.&amp;lt;patch&amp;gt;&lt;/code&gt; scheme, we express the possibility of installing all the versions of &lt;em&gt;myDependencyA&lt;/em&gt; from "0.0.1" to "0.0.9". This therefore means that any version of the dependency that respects the range is considered valid. On the other hand, this also means that if a developer A installs the dependency at 2 p.m. and a developer B installs the dependency at 5 p.m., they may both not have the same dependency tree if ever a new version of &lt;em&gt;myDependencyA&lt;/em&gt; is released in the meantime.&lt;/p&gt;

&lt;p&gt;The npm dependency resolution algorithm will by default favor the installation of the most recent dependency that respects the semantic management described in the &lt;em&gt;package.json&lt;/em&gt;. By specifying &lt;code&gt;npm install myDependencyA&lt;/code&gt;, the most recent version of &lt;em&gt;myDependencyA&lt;/em&gt; will be installed respecting the constraint "&amp;lt;1.0.0" (version strictly lower than "1.0.0").&lt;/p&gt;

&lt;p&gt;The major problem with this approach &lt;strong&gt;is the lack of stability and reproducibility of the dependency tree from one computer to another&lt;/strong&gt;, for example between developers or even on the machine used in production. Imagine that version 0.0.9 of &lt;em&gt;myDependencyA&lt;/em&gt; has just been released with a bug and your production machine is about to do an &lt;code&gt;npm install&lt;/code&gt; on Friday at 5:59 PM…&lt;/p&gt;

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

&lt;p&gt;The very simple example is often referred as &lt;code&gt;version drift&lt;/code&gt;. This is why a single description file (in this case package.json) &lt;strong&gt;cannot be enough to guarantee an identical and reproducible representation of a dependency tree&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Other reasons include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using a different version of the package manager whose dependency installation algorithm may change.&lt;/li&gt;
&lt;li&gt;publishing a new version of an indirect dependency (the dependencies of the dependencies we list in the package.json here), which would result in the new version therefore being uploaded and updated.&lt;/li&gt;
&lt;li&gt;the use of a different registry which for the same version of a dependency exposes two different libraries at a time T.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lockfiles to the rescue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To ensure the reproducibility of a dependency tree, we therefore need more information that &lt;strong&gt;would ideally describe the current state of our dependency tree&lt;/strong&gt;. This is exactly what lockfiles do. These are files created and updated when the dependencies of a project are modified.&lt;/p&gt;

&lt;p&gt;A lockfile is generally written in &lt;em&gt;JSON&lt;/em&gt; or &lt;em&gt;YAML&lt;/em&gt; format to simplify the readability and understanding of the dependency tree by a human. A lockfile makes it possible to describe the dependency tree in a very precise way and &lt;strong&gt;therefore to make it deterministic and reproducible from one environment to another&lt;/strong&gt;. So it's important to commit this file to Git and make sure everyone is sharing the same lockfile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;package-lock.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myProject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"myDependencyA"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://registry.npmjs.org/myDependencyA/-/myDependencyA-0.0.5.tgz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha512-DeAdb33F+"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://registry.npmjs.org/B/-/B-0.0.1.tgz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha512-DeAdb33F+"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;B&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For npm, the basic lockfile is called &lt;em&gt;package-lock.json&lt;/em&gt;. In the snippet above, we can precisely see several important information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The version of myDependencyA is fixed at "0.0.5" so even if a new version is released, npm will install "0.0.5" no matter what.&lt;/li&gt;
&lt;li&gt;Each indirect dependency describes its set of dependencies with versions that also describe their own versioning constraints.&lt;/li&gt;
&lt;li&gt;In addition to the version, the contents of the dependencies can be checked with the comparison of hashes which can vary according to the registers used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lockfile therefore tries to accurately describes the dependency tree, which allows it to remain consistent and reproducible over time at each installation.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;But...&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lockfiles don't solve all inconsistency problems! Package managers implementations of the dependency graph can sometimes lead to inconsistencies. For a long time, npm's implementation introduced &lt;a href="https://rushjs.io/pages/advanced/phantom_deps/" rel="noopener noreferrer"&gt;Phantom Dependencies&lt;/a&gt; and also &lt;a href="https://rushjs.io/pages/advanced/npm_doppelgangers/" rel="noopener noreferrer"&gt;NPM doppelgangers&lt;/a&gt; which are very well explained on the &lt;a href="https://rushjs.io/" rel="noopener noreferrer"&gt;Rush.js&lt;/a&gt; documentation website (advanced topics that are out of the scope of this blog post).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Provision of distributed and transparent databases via open-source
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Distributed registries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A package manager is a client that acts as a gateway to a distributed database (often called a registry). This allows in particular to share an infinite number of open-source libraries around the world. It is also possible to define company-wide private registries in a secured network, within which libraries would be accessible. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/verdaccio/verdaccio" rel="noopener noreferrer"&gt;Verdaccio&lt;/a&gt; allows to setup a private proxy registry for Node.js&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The availability of registries has greatly changed the way software is developed by facilitating access to millions of libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transparent access to resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The other benefit of open-source package managers is that they most often expose platforms or tools that allow browsing through published packages. Accessing source code and documentation has been trivialized and made very transparent. It is therefore possible for each developer to have an overview or even to fully investigate the code base of a published library.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Security and integrity
&lt;/h2&gt;

&lt;p&gt;Using open-source registries with millions of publicly exposed libraries is pretty convenient, but what about &lt;em&gt;security&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;It is true that open-source registries represent ideal targets for hackers: &lt;a href="https://therecord.media/malware-found-in-npm-package-with-millions-of-weekly-downloads/" rel="noopener noreferrer"&gt;all you have to do is take control of a widely used library (downloaded millions of times a week) and inject malicious code into it, and no one will realize!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this part, we will see the solutions implemented by package managers and registries to deal with these attacks and limit the risks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrity safety for each installed package&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Given that a package can be installed from any registry, it is important to implement verification mechanisms at the level of the content of the downloaded package, to ensure that no malicious code has been injected during the download, regardless of its origin.&lt;/p&gt;

&lt;p&gt;For this, integrity metadata is associated with each installed package. For example with npm, an integrity property is associated with each package in the lockfile. This property contains a cryptographic hash which is used to accurately represent the resource the user expects to receive. This allows any program to verify that the content of the resource matches what was downloaded. For example for &lt;code&gt;@babel/core&lt;/code&gt;, this is how integrity is represented in package-lock.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"@babel/core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7.16.10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://registry.npmjs.org/@babel/core/-/core-7.16.10.tgz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
   &lt;/span&gt;&lt;span class="nl"&gt;"integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha512 pbiIdZbCiMx/MM6toR+OfXarYix3uz0oVsnNtfdAGTcCTu3w/JGF8JhirevXLBJUu0WguSZI12qpKnx7EeMyLA=="&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take a closer look at how integrity can drastically reduce the risk of injecting malicious code by hashing source code.&lt;/p&gt;

&lt;p&gt;As a reminder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We call hash function, a particular function which, from a datum supplied as input, calculates a digital fingerprint used to quickly identify the initial datum, in the same way as a signature to identify a person. &lt;a href="https://en.wikipedia.org/wiki/Hash_function" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's take for example a simple case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my-library&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;someJavaScriptCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's imagine that this JavaScript code represents a resource that a user might want to download. Using the &lt;em&gt;SHA1&lt;/em&gt; hash function, we get the hash &lt;code&gt;7677152af4ef8ca57fcb50bf4f71f42c28c772be&lt;/code&gt;.&lt;br&gt;
If ever malicious code is injected, the library's fingerprint will by definition change because the input (source code here) to the hash function will have changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my-library&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;someJavaScriptCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;processMaliciousCode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// this is injected, the user is not  expecting that&lt;/span&gt;
  &lt;span class="nf"&gt;addUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After injecting the malicious code, still using the same &lt;em&gt;SHA1&lt;/em&gt; hash function, we obtain &lt;code&gt;28d32d30caddaaaafbde0debfcd8b3300862cc24&lt;/code&gt; as the digital fingerprint.&lt;br&gt;
So we get as results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Original code = &lt;code&gt;7677152af4ef8ca57fcb50bf4f71f42c28c772be&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Malicious code = &lt;code&gt;28d32d30caddaaaafbde0debfcd8b3300862cc24&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All package managers implement strict specifications on this approach to integrity. For example, npm respects the W3C's "Subresource Integrity or SRI" specification, which describes the mechanisms to be implemented to reduce the risk of malicious code injection.&lt;br&gt;
You can jump directly &lt;a href="https://w3c.github.io/webappsec-subresource-integrity/" rel="noopener noreferrer"&gt;here&lt;/a&gt; to the specification document if you want to dig deeper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security constraints at the author level&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To strengthen security at the level of open-source packages, more and more constraints are emerging on the side of project authors and maintainers. Recently, GitHub, which owns npm, announced that it is &lt;a href="https://github.blog/2022-02-01-top-100-npm-package-maintainers-require-2fa-additional-security/" rel="noopener noreferrer"&gt;forcing two-factor authentication (2FA) for contributors to the 100 most popular packages&lt;/a&gt;. The main idea around these actions is to secure resources upstream by limiting write access to open-source packages and identifying people more precisely.&lt;/p&gt;

&lt;p&gt;It's important to also mention that there are tools that can be used to perform automatically scans and audits continuously. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built-in tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order to automate the detection of vulnerabilities, many package managers natively integrate tools allowing to scan the installed libraries. Typically, these package managers communicate with databases that list all known and referenced vulnerabilities. For example, &lt;a href="https://github.com/advisories" rel="noopener noreferrer"&gt;GitHub Advisory Database&lt;/a&gt; is an open-source database that references thousands of vulnerabilities across multiple ecosystems (Go, Rust, Maven, NuGet, etc) e.g. &lt;code&gt;npm audit&lt;/code&gt; command uses this database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-party tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/NodeSecure" rel="noopener noreferrer"&gt;NodeSecure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;NodeSecure&lt;/strong&gt; we are building free open source tools to secure the Node.js &amp;amp; JavaScript ecosystem. Our biggest area of expertise is in package and code analysis.&lt;/p&gt;

&lt;p&gt;Here are some example of the available tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/NodeSecure/cli" rel="noopener noreferrer"&gt;@nodesecure/cli&lt;/a&gt;, a CLI that allow you to deeply analyze the dependency tree of a given package or local Node.js project&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NodeSecure/js-x-ray" rel="noopener noreferrer"&gt;@nodesecure/js-x-ray&lt;/a&gt;, a SAST scanner (A static analyser for detecting most common malicious patterns)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NodeSecure/vulnera" rel="noopener noreferrer"&gt;@nodesecure/vulnera&lt;/a&gt;, a Software Component Analysis (SCA) tool&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NodeSecure/ci" rel="noopener noreferrer"&gt;@nodesecure/ci&lt;/a&gt;, a tool allowing to run SAST, SCA and many more analysis in CI/CDs or in a local environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://snyk.io/" rel="noopener noreferrer"&gt;Snyk&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Snyk is the most popular all-around solution for securing applications or cloud-based infrastructures. Snyk &lt;a href="https://snyk.io/plans/" rel="noopener noreferrer"&gt;offers a free-tier&lt;/a&gt; with SAST and SCA analysis.&lt;/p&gt;

&lt;p&gt;To ensure continuous detection of vulnerabilities, it is recommended to run scans each time packages are installed/modified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There you go, you now know what issues are addressed and solved by package managers!&lt;/p&gt;

&lt;p&gt;Package managers are complex tools that aim to make life easier for us as developers, but can quickly become problematic if misused.&lt;/p&gt;

&lt;p&gt;It is therefore important to understand the issues they deal with and the solutions provided in order to be able to put into perspective several package managers of the same ecosystem. In the end, it's a tool like any other and it must mobilize thinking in the same way as when libraries/frameworks/programming languages ​​are used.&lt;/p&gt;

&lt;p&gt;Don't also forget to take into account security issues and use automated tools which can drastically reduce the attack surface!&lt;/p&gt;

</description>
      <category>npm</category>
      <category>node</category>
      <category>opensource</category>
    </item>
    <item>
      <title>🕶 What it takes to build a Static Analysis tool</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Mon, 17 Oct 2022 09:40:00 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/what-it-takes-to-build-a-static-analysis-tool-4p40</link>
      <guid>https://dev.to/antoinecoulon/what-it-takes-to-build-a-static-analysis-tool-4p40</guid>
      <description>&lt;p&gt;Hey dear developer! Welcome to the first chapter of &lt;strong&gt;My journey of building Skott, an open-source Node.js library&lt;/strong&gt;, a little series in which I'll talk about different steps I've been through building the &lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;Skott project&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before diving into the subject of today, &lt;strong&gt;&lt;a href="https://dev.to/antoinecoulon/introducing-skott-the-new-madge-1bfl"&gt;here is the blog post introducing the project&lt;/a&gt;&lt;/strong&gt; which is not mandatory to read to make this article valuable though, but it can help providing some background.&lt;/p&gt;

&lt;p&gt;Ok without further ado, let's talk about the topic of the day: &lt;strong&gt;building a static analysis tool&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Static Analysis?
&lt;/h3&gt;

&lt;p&gt;A Static Analysis represents the process of analyzing the source code without running the application. You probably know many tools leveraging Static Analysis behind the scenes for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript/Node.js: &lt;a href="https://github.com/eslint/eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python: &lt;a href="https://github.com/PyCQA/pylint" rel="noopener noreferrer"&gt;Pylint&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rust: &lt;a href="https://github.com/rust-lang/rust-clippy" rel="noopener noreferrer"&gt;Clippy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Lint_(software)" rel="noopener noreferrer"&gt;Linters&lt;/a&gt; are a set of tools fully leveraging Static Analyzes to flag programming errors, bugs, stylistic errors and suspicious constructs without having to run the code.&lt;/p&gt;

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

&lt;p&gt;As you can see on the image above, ESLint is able to detect &lt;em&gt;unused variables&lt;/em&gt; and flag them as errors (according to the provided configuration).&lt;/p&gt;

&lt;h3&gt;
  
  
  How does the Static Analysis work behind the scenes?
&lt;/h3&gt;

&lt;p&gt;The answer to that question will be done in three steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. What is a Parser&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2. What is an Abstract Syntax Tree&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;3. How to use an Abstract Syntax Tree&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Building static analysis tools is not a trivial task, so we'll only scratch the surface here, but you should have some basics to go further if you want!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1. What is a Parser
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Parser&lt;/strong&gt; is a program generating an &lt;em&gt;intermediary data structure&lt;/em&gt; from an input string. Parsing is a is most typically done in two phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lexical Analysis&lt;/strong&gt; (also known as Lexer, Tokenizer)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal of this &lt;strong&gt;tokenization&lt;/strong&gt; phase is to generate &lt;em&gt;tokens&lt;/em&gt; from the input program, which is only a raw string at this point (any programming language file in fact, .js, .rs, .go, .py, etc).&lt;/p&gt;

&lt;p&gt;Tokens are a collection of characters allowing to describe pieces of code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Syntactic Analysis&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following the &lt;strong&gt;tokenization&lt;/strong&gt;, the &lt;strong&gt;Syntactic Analysis&lt;/strong&gt; takes produced tokens and generates an &lt;em&gt;intermediary data structure describing precisely these tokens and their relationships&lt;/em&gt;. Spoiler: this intermediary data structure is most of the time an &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" rel="noopener noreferrer"&gt;Abstract Syntax Tree (AST)&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is a schema of a common parsing process:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  2. What is an Abstract Syntax Tree
&lt;/h3&gt;

&lt;p&gt;As already said in the previous section, an &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" rel="noopener noreferrer"&gt;Abstract Syntax Tree&lt;/a&gt;&lt;/strong&gt; is one of the data structure that can be generated while parsing source code (other structures can be used instead such as &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Parse_tree" rel="noopener noreferrer"&gt;Concrete Syntax Trees&lt;/a&gt;&lt;/strong&gt; depending on the use case).&lt;/p&gt;

&lt;p&gt;The goal of this intermediate representation is to have a &lt;strong&gt;standardized way of representing the code&lt;/strong&gt; that is easy to work with (but without any loss of information).&lt;/p&gt;

&lt;p&gt;The most important keyword here is probably &lt;strong&gt;standardized&lt;/strong&gt;, which is one of the main characteristics an AST should respect to be useful. As the AST is an intermediary data structure, the overall goal is to be able to &lt;strong&gt;transform the tree to anything we want&lt;/strong&gt; for example transform the tree to produce a program in an entirely new language e.g. &lt;strong&gt;&lt;a href="https://www.scala-js.org/" rel="noopener noreferrer"&gt;generating JavaScript from Scala&lt;/a&gt;&lt;/strong&gt; or most commonly JavaScript from TypeScript, etc 😎&lt;/p&gt;

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

&lt;p&gt;To allow that, each ecosystem/language has strict specifications describing what should be the standard shape of its own Abstract Syntax Tree. For the JavaScript language, this is the &lt;strong&gt;&lt;a href="https://github.com/estree/estree" rel="noopener noreferrer"&gt;ESTree spec&lt;/a&gt;&lt;/strong&gt;. This standardized format has the advantage of allowing any type of Parser (written using any programming language) to produce a common AST (following the ESTree spec) that can be then interpreted by any &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Interpreter_(computing)" rel="noopener noreferrer"&gt;Interpreter&lt;/a&gt;&lt;/strong&gt; (written in any language).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. How to use an Abstract Syntax Tree
&lt;/h3&gt;

&lt;p&gt;Generally speaking for compilers, an Abstract Syntax Tree is as I said multiple times an &lt;em&gt;intermediary data structure&lt;/em&gt; which can be then transformed to produce byte code or any other source target e.g. &lt;strong&gt;&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/strong&gt; emits JavaScript using its own AST.&lt;/p&gt;

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

&lt;p&gt;Nevertheless in the context of a Static Analysis, an Abstract Syntax Tree will most likely represent our &lt;em&gt;final data structure&lt;/em&gt; needed as it will allow us to inspect the source code patterns directly from it (no need further transformations that a compiler would do).&lt;/p&gt;

&lt;p&gt;By using the spec describing the shape of the AST, we're able (as Static Analysis tools developers) to rely on it to determine whatever static rule/condition, it's &lt;em&gt;just&lt;/em&gt; a matter of finding data structure combinations.&lt;/p&gt;

&lt;p&gt;To explore a little bit further the structure of ASTs for various languages, I like a lot &lt;strong&gt;&lt;a href="https://astexplorer.net/" rel="noopener noreferrer"&gt;AST Explorer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's wrap it up by example
&lt;/h3&gt;

&lt;p&gt;To demonstrate a little Static Analysis tool in action, I'll show one use case of &lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;Skott, a library that builds a Directed Graph of relationships between files of a Node.js project&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are not fully sure of the purpose of Directed Graphs, &lt;a href="https://dev.to/antoinecoulon/master-directed-graphs-by-example-with-javascript-4oef"&gt;you can check series I wrote on the topic here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To build that Directed Graph, Skott has to:&lt;br&gt;
&lt;strong&gt;1. Use the entrypoint of the project and parse it&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2. Statically find imports/exports statements of the entrypoint from the AST&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;3. Recursively follow imported/exported files and keep doing it until all files have been discovered&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's do this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use the entrypoint of the project and parse it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.js&lt;/strong&gt;&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;runMain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./program.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;makeDependencies&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dependencies.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// do something with runMain and makeDependencies&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Once we read the entrypoint file, we can use Skott to extract &lt;code&gt;import&lt;/code&gt; statements (here is above a simplified snippet &lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott/blob/870803a66b0589d1e9b0f7b1f30559532fe63956/packages/skott/src/modules/walkers/javascript/walker.ts#L8" rel="noopener noreferrer"&gt;from the Skott's codebase&lt;/a&gt;)&lt;/strong&gt;:&lt;/p&gt;

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

&lt;p&gt;At line 3, we import &lt;code&gt;parseScript&lt;/code&gt; from &lt;strong&gt;&lt;a href="https://github.com/meriyah/meriyah/" rel="noopener noreferrer"&gt;meriyah&lt;/a&gt;&lt;/strong&gt; which is a JavaScript parser (could have been &lt;a href="https://babeljs.io/docs/en/babel-parser" rel="noopener noreferrer"&gt;babel&lt;/a&gt;, &lt;a href="https://github.com/acornjs/acorn" rel="noopener noreferrer"&gt;acorn&lt;/a&gt;, &lt;a href="https://github.com/swc-project/swc" rel="noopener noreferrer"&gt;swc&lt;/a&gt;, etc doesn't matter for our use case as long as they correctly implement the spec).&lt;/p&gt;

&lt;p&gt;After that, we can &lt;em&gt;parse&lt;/em&gt; the file content that will return us the root node of the &lt;em&gt;Abstract Syntax Tree&lt;/em&gt;. Once the &lt;em&gt;AST&lt;/em&gt; is generated by &lt;em&gt;meriyah&lt;/em&gt;, the only thing left to do is to traverse the whole tree recursively and find the &lt;code&gt;import statements&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To simplify the example, we only track &lt;a href="https://nodejs.org/api/esm.html" rel="noopener noreferrer"&gt;ECMAScript modules&lt;/a&gt; here, but Skott also tries to deal with CommonJS modules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;How to find an import statement from within the AST?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay so now you might wonder how we can use the AST data structure to find &lt;code&gt;import statements&lt;/code&gt; 🧐&lt;/p&gt;

&lt;p&gt;Let's first take a look at the &lt;strong&gt;&lt;a href="https://github.com/estree/estree/blob/master/es2015.md" rel="noopener noreferrer"&gt;ESTree spec for es2015&lt;/a&gt;&lt;/strong&gt; to see how an &lt;code&gt;import statement&lt;/code&gt; is represented in the AST:&lt;/p&gt;

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

&lt;p&gt;Well, pretty simple!&lt;br&gt;
Anytime we meet a node with &lt;code&gt;type === "ImportDeclaration"&lt;/code&gt; we know that's an &lt;strong&gt;import statement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So here's the function to detect if an AST node is a &lt;em&gt;es2015 import statement&lt;/em&gt;:&lt;/p&gt;

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

&lt;p&gt;Great, we're now able to find &lt;em&gt;all import statements&lt;/em&gt; from any JavaScript file! Using the AST we could also get back information about where the import is located in the file and make it red highlighted in &lt;em&gt;vscode&lt;/em&gt; for instance (but I'll let that &lt;em&gt;vscode&lt;/em&gt; thing for 2023).&lt;/p&gt;

&lt;p&gt;Thanks to our &lt;em&gt;wonderful Skott performing Static Analysis&lt;/em&gt;, we're now able to generate and display the graph from all collected imports:&lt;/p&gt;

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

&lt;p&gt;As we can see, three nodes have been found (the only three files of the sample project) and dependencies have been established between &lt;code&gt;index.js&lt;/code&gt; and its two children being imported!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;If you made it this far, thanks, I appreciate it :) But it also means that you've probably understood foundations about Parsers and Abstract Syntax Trees and that's even nicer!&lt;/p&gt;

&lt;p&gt;Be sure to follow me if you're interested in discovering other topics I've covered throughout the journey of building Skott 💥&lt;/p&gt;

&lt;p&gt;For the next episode of this series, I'm planning to talk about &lt;code&gt;Test-Driven Development &amp;amp; Dependency Injection&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;Last but not least don't hesitate to bring stars ⭐️, issues, feedbacks directly on GitHub here&lt;/a&gt;&lt;/strong&gt; as it will allow me to drive the project where it will bring the most value for each developer.&lt;/p&gt;

&lt;p&gt;Wish everyone a wonderful day ☀️&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>compilers</category>
    </item>
    <item>
      <title>💥 Introducing Skott, the new Madge!</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Mon, 12 Sep 2022 07:49:34 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/introducing-skott-the-new-madge-1bfl</link>
      <guid>https://dev.to/antoinecoulon/introducing-skott-the-new-madge-1bfl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Little announcement: thanks for anyone showing interest in skott! Just for your own information, this article was not recently updated to include all the latest features that skott has, but it still remains a good introduction to the spirit of the project. Feel free to check the GitHub repository to see the latest CLI/API versions! &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hello everyone, hope you're doing well!&lt;/p&gt;

&lt;p&gt;Today I'm very pleased to share a fun project I have been working on for several months now: &lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;skott&lt;/a&gt;&lt;/strong&gt;. &lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;skott&lt;/a&gt;&lt;/strong&gt; is a tool that generates a graph of dependencies from your &lt;strong&gt;JavaScript/TypeScript/Node.js&lt;/strong&gt; projects (&lt;em&gt;including TSX/JSX and ES6/CommonJS modules!&lt;/em&gt;) and helps you discover &lt;strong&gt;circular dependencies, cost imports, file sizes, and many more&lt;/strong&gt; 💥&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Node.js builtin&lt;/em&gt; and/or &lt;em&gt;npm third-party dependencies&lt;/em&gt; can also be configured to be added in the graph. The main goal is to reproduce the architecture of your application at the file level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation and use
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;skott
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;skott&lt;/strong&gt; provides an API to traverse the project graph, deeply search for circular dependencies, find parents or children for a given set of nodes, etc.&lt;/p&gt;

&lt;p&gt;Here is a quick overview of the API:&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;skott&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skott&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;getStructure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;findCircularDependencies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;findParentsOf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;findLeaves&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;skott&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * The entrypoint of the project. Must be either a CommonJS or ES6 module.
   * For now, TypeScript files are not supported as entrypoints.
   */&lt;/span&gt; 
  &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Define the max depth of for circular dependencies search. This can be useful 
   * for performance purposes. This defaults to POSITIVE_INFINITY.
   */&lt;/span&gt;
  &lt;span class="na"&gt;circularMaxDepth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * This defines whether the base directory of the entrypoint must be included
   * in all the relatives file paths.
   * For the specified `dist/index.js` above, it would consider the root path
   * to be `./` consequently `dist/` would never appear in any file paths.
   */&lt;/span&gt;
  &lt;span class="na"&gt;includeBaseDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;skott&lt;/strong&gt; can also be used through the CLI. &lt;/p&gt;

&lt;h2&gt;
  
  
  Web Visualization
&lt;/h2&gt;

&lt;p&gt;Using the webapp display mode &lt;code&gt;skott --displayMode=webapp&lt;/code&gt;, &lt;strong&gt;skott&lt;/strong&gt; generates the graph and then automatically opens an interactive web application in which you can visualize the graph more precisely.&lt;/p&gt;

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

&lt;p&gt;As shown above, we can see all the nodes created from files of your project, and edges are simply representing the links between files of your project. &lt;strong&gt;Circular dependencies&lt;/strong&gt;, &lt;strong&gt;third-party dependencies&lt;/strong&gt; and &lt;strong&gt;built-in dependencies&lt;/strong&gt; can be displayed on demand to adapt the amount of information displayed at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Console Visualization
&lt;/h2&gt;

&lt;p&gt;Sometimes, you don't want to open a web interface to check what you want. For that, you can use other display mode that will render an UI in the console. Here is a quick preview of the graph generated for the &lt;strong&gt;&lt;a href="https://github.com/fastify/fastify" rel="noopener noreferrer"&gt;fastify&lt;/a&gt;&lt;/strong&gt; library:&lt;/p&gt;

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

&lt;p&gt;We can also decide to track more dependencies, for example the &lt;em&gt;npm third-party dependencies&lt;/em&gt; (by providing the "&lt;strong&gt;--trackThirdPartyDependencies&lt;/strong&gt;" option) &lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Node.js builtin dependencies can also be tracked by providing the &lt;strong&gt;"--trackBuiltinDependencies"&lt;/strong&gt; option.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  File tree display
&lt;/h3&gt;

&lt;p&gt;Different modes of display are available from the CLI, including &lt;strong&gt;file-tree&lt;/strong&gt; which reconstructs a directory of files from the graph which is far more concise:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Static file generation
&lt;/h3&gt;

&lt;p&gt;In addition to various display modes from the CLI, Skott is also able to create static files reflecting your project graph, including &lt;em&gt;.json&lt;/em&gt;, &lt;em&gt;.svg&lt;/em&gt;, &lt;em&gt;.md.&lt;/em&gt; (using mermaid), and &lt;em&gt;.png&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example creating a static file from &lt;code&gt;skott&lt;/code&gt; itself:&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;skott dist/index.js &lt;span class="nt"&gt;--staticFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;svg


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

&lt;/div&gt;

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

&lt;p&gt;For medium to big size projects, you'll probably want to use the &lt;code&gt;webapp&lt;/code&gt; display mode! 🫣&lt;/p&gt;

&lt;h3&gt;
  
  
  Circular dependencies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;skott&lt;/a&gt;&lt;/strong&gt; also helps finding out &lt;strong&gt;circular dependencies very efficiently&lt;/strong&gt; in the project, you can even provide a max depth search to avoid deep searches that could be costly.&lt;/p&gt;

&lt;p&gt;If you're not sure to see why circular dependencies &lt;em&gt;can be&lt;/em&gt; problematic, I created a section &lt;strong&gt;Why you should care about circular dependencies and dead code in the &lt;a href="https://github.com/antoine-coulon/skott#readme" rel="noopener noreferrer"&gt;root documentation of skott&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Some options can also be provided to the CLI to configure the exit code that will be used when circular dependencies are met (defaults to "1" meaning error exit).&lt;/p&gt;

&lt;h3&gt;
  
  
  Skott is fast
&lt;/h3&gt;

&lt;p&gt;We can easily compare &lt;strong&gt;&lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;skott&lt;/a&gt;&lt;/strong&gt; with &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/madge" rel="noopener noreferrer"&gt;madge&lt;/a&gt;&lt;/strong&gt; because &lt;strong&gt;skott&lt;/strong&gt; already covers most of the features &lt;strong&gt;madge&lt;/strong&gt; exposes for a Node.js project.&lt;/p&gt;

&lt;p&gt;I did some benchmarks about the computing time required to build a set of graphs from popular libraries for both &lt;strong&gt;skott&lt;/strong&gt; and &lt;strong&gt;madge&lt;/strong&gt; and here are the results:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/webpack/webpack" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; (+560 files)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;webpack&lt;/strong&gt; is a static module bundler for modern JavaScript applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Webpack is probably one of the heaviest open-source Node.js project I know. So let's do a benchmark!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using &lt;strong&gt;skott&lt;/strong&gt; takes &lt;strong&gt;503ms&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;using &lt;strong&gt;madge&lt;/strong&gt; takes &lt;strong&gt;2.5 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;N.B: the difference of files between skott and madge is only because of .json files that are ignored by skott in the graph (as well as other files such as binaries).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/knex/knex" rel="noopener noreferrer"&gt;Knex.js&lt;/a&gt; (+6O files)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;knex.js&lt;/strong&gt; A SQL query builder that is flexible, portable, and fun to use! &lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;using &lt;strong&gt;skott&lt;/strong&gt; takes &lt;strong&gt;60ms&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjry447wsv9579ts4le77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjry447wsv9579ts4le77.png" alt="knex benchmark using skott"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;using &lt;strong&gt;madge&lt;/strong&gt; takes &lt;strong&gt;450ms&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyuyyt1yzq4sfvlty8yj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyuyyt1yzq4sfvlty8yj.png" alt="knex benchmark using madge"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For building the entire graph of &lt;strong&gt;knex.js&lt;/strong&gt; with even more metadata, &lt;strong&gt;skott is 7.5 times faster!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/fastify/fastify" rel="noopener noreferrer"&gt;Fastify.js&lt;/a&gt; (30 files)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;fastify.js&lt;/strong&gt; is a fast and low overhead web framework, for Node.js&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;using &lt;strong&gt;skott&lt;/strong&gt; takes &lt;strong&gt;50ms&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;using &lt;strong&gt;madge&lt;/strong&gt; takes &lt;strong&gt;350ms&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In this case, &lt;strong&gt;skott&lt;/strong&gt; is 7 times faster than &lt;strong&gt;madge&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skott is extensible
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;skott&lt;/strong&gt; aims to offer features that could be extended to any language, given that specific parsers can be implemented along the way. I started from only JavaScript, incrementally brought TypeScript and TSX/JSX support along the way. Why not bringing other languages around the table?&lt;/p&gt;

&lt;h3&gt;
  
  
  Stay tuned
&lt;/h3&gt;

&lt;p&gt;Many more features will be implemented (&lt;strong&gt;skott does not yet have a major version released&lt;/strong&gt;) to help you easily discover and demystify what's going on under the hood of your Node.js project! I'm also thinking about developing a &lt;em&gt;Web Application&lt;/em&gt; that will help you visualize graphs and every metadata related to each file (number of imports, file size, unused imports, etc).&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharing journey of building an open-source library
&lt;/h3&gt;

&lt;p&gt;I'll also start a series about the journey of building &lt;strong&gt;skott&lt;/strong&gt;, which notably includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;[OUT] &lt;a href="https://dev.to/antoinecoulon/what-it-takes-to-build-a-static-analysis-tool-4p40"&gt;What it takes to build a static analysis tool (parsers, ast, etc)&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;How to make all of that &lt;strong&gt;fully testable by using dependency injection and Test-Driven Development&lt;/strong&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't hesitate to bring &lt;strong&gt;stars ⭐️, issues, feedbacks&lt;/strong&gt; &lt;a href="https://github.com/antoine-coulon/skott" rel="noopener noreferrer"&gt;directly on GitHub here&lt;/a&gt; as it will allow me to drive the project where it will bring the most value for each developer.&lt;/p&gt;

&lt;p&gt;Thanks for reading this far, wish everyone a wonderful day ☀️&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>node</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>🛠 Let's implement the Incremental/Affected pattern, the killer feature used by Turborepo, Nx, Rush and many more popular tools</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Tue, 29 Mar 2022 11:51:37 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/lets-implement-the-incrementalaffected-pattern-the-killer-feature-used-by-turborepo-nx-rush-and-many-more-popular-tools-4l51</link>
      <guid>https://dev.to/antoinecoulon/lets-implement-the-incrementalaffected-pattern-the-killer-feature-used-by-turborepo-nx-rush-and-many-more-popular-tools-4l51</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/antoinecoulon/master-directed-graphs-by-example-with-javascript-4oef"&gt;In the introduction of the &lt;strong&gt;Master Directed Graphs by example with JavaScript&lt;/strong&gt;&lt;/a&gt;, we saw how useful the directed graphs could be. Before reading this blog post, I highly recommend you to read it before going further in this series.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The goal of this series is to demonstrate the usefulness and power of directed graphs by implementing concrete examples with JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Let's start by introducing our today's topic
&lt;/h2&gt;

&lt;p&gt;Our today's objective is to explain in a simplified way how the Affected/Incremental pattern works. The whole idea of detecting affected projects/packages is to process tasks (build, lint, test) only on parts of project that change hence optimizing ressources and saving time when working on heavy projects.&lt;/p&gt;

&lt;p&gt;Take for instance a simple JavaScript frontend application (app.jsx) with two libs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;design system&lt;/strong&gt; library to export your custom UI components (e.g: by wrapping Material UI or Angular Material with custom themes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;date formatting&lt;/strong&gt; library to configure an universal set of rules to work with dates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our project would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-folder/
│
└───design-system/
|   |   dist/
│   │   component1.jsx

│
└───temporal/
│   |   dist/
│   │   dateFormat.js
|
|   dist/
|   app.jsx
|   package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;app.jsx&lt;/code&gt; represents our entry file and uses features from both &lt;code&gt;design-system&lt;/code&gt; and &lt;code&gt;temporal&lt;/code&gt; libs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.jsx&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Component1&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./design-system/component1.jsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hoursToMilliseconds&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./temporal/dateFormat.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;hoursToMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Component1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To build the entire application, imagine we have an npm script which bundles all libs and the entry point into a single JavaScript file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bundle the whole app including `design-system` and `temporal` libs&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might wonder what is the problem with this approach. The answer is that there is no problem yet and this type of bundling is probably fine most of the time for small to medium sized projects.&lt;/p&gt;

&lt;p&gt;However if your &lt;code&gt;design-system&lt;/code&gt; and &lt;code&gt;temporal&lt;/code&gt; end up growing and you even add more libraries (more components, but also including assets such as images, fonts etc) you might encounter performance issues when building the whole app.&lt;/p&gt;

&lt;p&gt;Let's demonstrate that very easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-folder/
│
└───design-system/ # 100+ components
│
└───temporal/ # 50+ files
|
└───lib3/ # library using node-sass (nearly 5 MB)
|
└───lib4/ # library using heavy npm modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say that this project takes X time in seconds to be entirely bundled. Now imagine you only change the color of your &lt;code&gt;design-system/component1.jsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="s2"&gt;`
  color: red;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! Your component now looks great... but you have to rebuild your whole app which also includes the rebuild of the heavy ones &lt;code&gt;lib3&lt;/code&gt; and &lt;code&gt;lib4&lt;/code&gt;.&lt;br&gt;
We have to build &lt;strong&gt;everything each time&lt;/strong&gt; we want to ship the main application while not everything has changed and therefore &lt;strong&gt;does not necessarily have to be rebuilt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This type of problem is often encountered in monorepos where many apps and libraries share the same build/test/lint tools.&lt;/p&gt;
&lt;h3&gt;
  
  
  Introducing the killer feature: the Incremental/Affected pattern
&lt;/h3&gt;

&lt;p&gt;If you're using monorepo tools such as &lt;a href="https://nx.dev/" rel="noopener noreferrer"&gt;Nx&lt;/a&gt;, &lt;a href="https://turborepo.org/" rel="noopener noreferrer"&gt;Turborepo&lt;/a&gt; or &lt;a href="https://rushjs.io/" rel="noopener noreferrer"&gt;Rush&lt;/a&gt;, you have probably heard of &lt;strong&gt;Incremental&lt;/strong&gt; and/or &lt;strong&gt;Affected&lt;/strong&gt; patterns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are not comfortable what is a monorepo and what are their objectives, I recommend you to take a look at a little reminder about it &lt;a href="https://monorepo.tools/#what-is-a-monorepo" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post, we will use the &lt;strong&gt;Affected&lt;/strong&gt; word but &lt;strong&gt;Incremental&lt;/strong&gt; means literally the same thing here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Ok, but what is the point with directed graphs?&lt;/strong&gt;&lt;br&gt;
The tool in charge of that (e.g: Turborepo, Nx, Rush) can introspect your project, and internally emit a Directed Acyclic Graph responsible for establishing dependencies between pieces of your project. By using the emitted Graph and a persisted cache (also handle by the tool), this enables smart builds and many other tasks that depends on the state of the project (linting, testing, etc).&lt;/p&gt;

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

&lt;p&gt;In the image above, we can see a project using an &lt;strong&gt;affected&lt;/strong&gt; strategy to build only what really needed to be rebuilt. When building the &lt;strong&gt;Main App&lt;/strong&gt; after &lt;strong&gt;Library 2&lt;/strong&gt; changed, only &lt;strong&gt;Library 1&lt;/strong&gt; and &lt;strong&gt;Library 2&lt;/strong&gt; must be rebuilt. The other part of the graph (&lt;strong&gt;Library 3&lt;/strong&gt;, &lt;strong&gt;Library 4&lt;/strong&gt;, &lt;strong&gt;Library 5&lt;/strong&gt;) &lt;strong&gt;remains unaffected&lt;/strong&gt; hence the cached version of these libraries can be used in the final build (no need to rebuild them).&lt;/p&gt;

&lt;p&gt;Let's start by implementing a minimalist version of the Affected pattern for a project containing with three distinct libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * lib1 depends on lib3 (via the use of lib3.MyLib3Component)
 * while library 2 is independent.
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib1Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;lib3.MyLib3Component/ &amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib2Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div&amp;gt;hello from lib2&amp;lt;/div&amp;gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib3Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;MyLib3Component&amp;gt;hello lib3&amp;lt;/MyLib3Component&amp;gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have to express our different components in our Graph context that we will use afterwards.&lt;br&gt;
Let's start by adding the core items of a graph: the vertices. A vertex can represent any type of item which in our case is a library of a given project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DiGraph&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;digraph-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectGraph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DiGraph&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Each project is represented with a vertex (node)&lt;/span&gt;
&lt;span class="nx"&gt;projectGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addVertices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lib2Metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lib3Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now that we added our vertices, we must represent relationships between them by adding appropriate vertices.&lt;br&gt;
In our example project, the &lt;code&gt;lib1&lt;/code&gt; depends on &lt;code&gt;lib3&lt;/code&gt; (by using the lib3 component). Consequently this import creates an implicit relationship between these nodes hence needs to be represented with an edge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * lib1 depends on lib3, we must represent this relationship
 * by adding an edge from lib1 to lib3.
 */&lt;/span&gt;
&lt;span class="nx"&gt;projectGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEdge&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lib3Metadata&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We just finished expressing our relationships between libraries of our project so we are now ready to implement our caching system in order to enable affected builds!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Simulating a simple cache, persisting an hashed
 * value of the component.
 * In a real world project, you would use something
 * like the "folder-hash" library to generate hashes
 * for a given folder containing sub directories and files.
 */&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;lib1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;lib2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;lib3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first function we need is a function to build a library.&lt;br&gt;
During that build process, its content is hashed and stored in the &lt;code&gt;cache&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create a SHA1 using the library content&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;libraryHashedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Building library: '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Webpack, Rollup or any other bundler can be processed here&lt;/span&gt;

  &lt;span class="c1"&gt;// Store the hashed content in cache&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;libraryHashedContent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using that cache, it's pretty straightforward to compare if a library changed. If we are able to detect if a library changed, it means that we are able to detect if it needs to be rebuilt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isLibraryAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;libraryHashedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;libraryHashedContent&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on once we change something in a library, we will be able to detect changes and invalidate the cache.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hashed build + Cache = Affected build
&lt;/h4&gt;

&lt;p&gt;Now that we're able to build a library, generate a hash from it and store it in a cache where build versions can be compared over time, we can put together the core function of the Affected pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * If the component is still the same (i.e: hash data hasnt changed), we
   * don't want to rebuild it. If its "Affected", we must invalidate it
   */&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isLibraryAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Component's hash changed, meaning we must build the library&lt;/span&gt;
    &lt;span class="nf"&gt;buildLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasLibraryBeenRebuilt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// The lib has not changed so does not require a new build&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Using cached version of '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasLibraryBeenRebuilt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try out to run an Affected build on a given library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build lib1 using Affected detection&lt;/span&gt;
&lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; prints out:  "Building library: 'lib1'";&lt;/span&gt;

&lt;span class="c1"&gt;// re-run a build without any changes multiple times&lt;/span&gt;
&lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; prints out each time: "Using cached version of 'lib1'";&lt;/span&gt;

&lt;span class="c1"&gt;// change lib1's content&lt;/span&gt;
&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;div&amp;gt; Hello lib1 &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// re-run a build&lt;/span&gt;
&lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; prints out:  "Building library: 'lib1'";&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok cool, we demonstrated how this pattern works for a given library. You may wonder why the Affected pattern is so useful in big projects including hundreds of libraries that are relying on each other. The answer is the Affected pattern proves to be extremely powerful and useful in a project where unnecessary computations are saved. If you're using a project with three libraries, it's probably not worth it to implement the Affected pattern which will cost you more CPU than building from scratch every time.&lt;/p&gt;

&lt;p&gt;Enough talk, let's continue our project example with a use case allowing us to feel its rising power.&lt;/p&gt;

&lt;h4&gt;
  
  
  Implementation of the Affected pattern using &lt;a href="https://github.com/antoine-coulon/digraph-js" rel="noopener noreferrer"&gt;digraph-js&lt;/a&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;digraph-js is a library which helps building directed graphs and allows us to traverse graphs effortlessly&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Following the example, we will now implement a more complete version of the pattern which includes multiple libraries detection using the &lt;strong&gt;digraph-js&lt;/strong&gt; library.&lt;/p&gt;

&lt;p&gt;As a quick reminder, we still have three libs in our project example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib1Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;lib3.MyLib3Component /&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib2Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div&amp;gt;hello lib2&amp;lt;/div&amp;gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib3Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lib3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;adjacentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;MyLib3Component&amp;gt;hello lib3&amp;lt;/MyLib3Component&amp;gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;lib1 depends on lib3 (lib1 uses "MyLib3Component")&lt;/li&gt;
&lt;li&gt;lib2 depends on nothing (no dependency)&lt;/li&gt;
&lt;li&gt;lib3 depends on nothing (no dependency)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What directed graphs allow us is that given a root library (can be any node in the graph), we can traverse all dependencies of that library using edges pointing towards other nodes of the graph. Using that feature, we are able to determine on which other libraries the root library depends on.&lt;/p&gt;

&lt;p&gt;Let's build a function whose goal is to build all dependencies of a given root node. For now, the root node does not know the status of all its children (either they have been rebuilt or not).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Given a root node, we traverse the graph looking for dependencies&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;buildAllRootLibraryDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootLibrary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Traverse all rootLibrary's dependencies&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootLibraryDependency&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;projectGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAdjacentVerticesTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;rootLibrary&lt;/span&gt;
  &lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * Recursively build affected libraries starting from the deepest dependencies
     * of the root library.
     */&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;buildAllLibraryDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootLibraryDependency&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * When we reach a dependency with no other dependencies, we
   * now that we're done going deep into the dependency tree.
   * If the library has been rebuilt, we must inform the
   * parent in order to recursively invalidate nodes in the
   * traversed path.
   */&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hasLibraryBeenRebuilt&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootLibrary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;hasLibraryBeenRebuilt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can traverse each dependency of the root node and rebuilt it if affected.&lt;br&gt;
Now, we must include the root node in that affected process by using the &lt;strong&gt;buildAllRootLibraryDependencies&lt;/strong&gt; function above.&lt;/p&gt;

&lt;p&gt;There are 2 conditions requiring the root library to be rebuilt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At least one of the dependencies of the library changed (can be determined by keeping track of each children state)&lt;/li&gt;
&lt;li&gt;The root library itself changed (same simple hash comparison mentioned in the first part of the blog post)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's implement that last function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Build everything with affected strategy including root
 * library itself
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildEverythingAffectedIncludingRootLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootLibrary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootLibraryDependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;projectGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAdjacentVerticesTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootLibrary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allRebuiltLibraries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dependencyLibrary&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rootLibraryDependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * Keep track of all the children builds to know if some
     * were rebuilt. If there is no affected dependency, the
     * array would remain empty
     */&lt;/span&gt;
    &lt;span class="nx"&gt;allRebuiltLibraries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;buildAllRootLibraryDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependencyLibrary&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * All root library's dependencies were rebuilt if necessary (i.e: affected).
   * However, we now need to determine if the root library has to also be
   * rebuilt. There are 2 conditions requiring the root library to be rebuilt:
   * - The root library itself changed
   * - Atleast one of the dependencies of the library changed
   */&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HAS_LIBRARY_BEEN_REBUILT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;atleastOneLibraryChanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allRebuiltLibraries&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HAS_LIBRARY_BEEN_REBUILT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;atleastOneLibraryChanged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;buildLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootLibrary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if library itself changed by running affected detection on the root library&lt;/span&gt;
    &lt;span class="nf"&gt;buildAffected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootLibrary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! The &lt;strong&gt;Affected&lt;/strong&gt; pattern is now fully demonstrated using directed graphs to traverse all dependencies of a given root node and using a cache comparison to determine if a child node must be invalidated then rebuilt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're not convinced? Let's confirm that by testing with a function:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildProjectUsingAffectedStrategy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;----STEP 1-----&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Building for the first time&lt;/span&gt;
  &lt;span class="nf"&gt;buildEverythingAffectedIncludingRootLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;----STEP 2-----&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Building for the second time but no dependencies of lib1 changed (neither
   * lib3 or lib4) so it remains UNAFFECTED (i.e: using cache)
   */&lt;/span&gt;
  &lt;span class="nf"&gt;buildEverythingAffectedIncludingRootLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;----STEP 3-----&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Let's now change the content of lib3's component.
   * Remember, lib1 depends on lib3 via the use of lib3.MyLib3Component so
   * this change should trigger an affected build.
   */&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Changing lib3's content...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// addMutation is a function which update the value of a given vertex in the graph&lt;/span&gt;
  &lt;span class="nx"&gt;projectGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib3Metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// new lib3 component&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;MyLib3Component&amp;gt;Hello affected lib3!&amp;lt;/MyLib3Component&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;----STEP 4-----&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Now that lib3 (dependency of lib1) changed, both lib3 and lib1 are considered
   * affected. It means that we must rebuild both, starting with lib3 (lib1 must be built
   * with the latest version of lib3).
   */&lt;/span&gt;
  &lt;span class="nf"&gt;buildEverythingAffectedIncludingRootLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lib1Metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;----STEP 1-----
Building library: 'lib3' // building for the first time
Building library: 'lib1' // building for the first time

----STEP 2-----
Using cached version of 'lib3' // building for the second time but nothing changed so we can use a cached version of lib3
Using cached version of 'lib1' // building for the second time but nothing changed neither from lib3 nor from lib1 itself so we can use a cached version of lib1

----STEP 3-----
Changing lib3's content... // updating lib3 content

----STEP 4-----
Building library: 'lib3' // lib3 changed so must be rebuilt
Building library: 'lib1' // lib1 did not change but its direct dependency "lib3" changed so it must be rebuilt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's finally it! We demonstrated the big picture of how the Affected pattern works under the hood.&lt;/p&gt;

&lt;h4&gt;
  
  
  GitHub repository
&lt;/h4&gt;

&lt;p&gt;Feel free to check the &lt;a href="https://github.com/antoine-coulon/digraph-js/tree/master/examples/affected-builds" rel="noopener noreferrer"&gt;full example here&lt;/a&gt; used in the final part of the blog post.&lt;/p&gt;

&lt;h4&gt;
  
  
  Coming next
&lt;/h4&gt;

&lt;p&gt;In the next part of this series, we will talk about &lt;code&gt;circular dependency detection&lt;/code&gt; (which is for instance implemented by&lt;br&gt;
an ESLint plugin &lt;a href="https://github.com/import-js/eslint-plugin-import/blob/main/src/rules/no-cycle.js" rel="noopener noreferrer"&gt;eslint-plugin-import/no-cycle alike&lt;/a&gt;) that are made possible with &lt;strong&gt;directed graphs&lt;/strong&gt; !&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>computerscience</category>
      <category>node</category>
    </item>
    <item>
      <title>🔀 Master Directed Graphs by example with JavaScript (introduction)</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Tue, 15 Mar 2022 11:51:23 +0000</pubDate>
      <link>https://dev.to/antoinecoulon/master-directed-graphs-by-example-with-javascript-4oef</link>
      <guid>https://dev.to/antoinecoulon/master-directed-graphs-by-example-with-javascript-4oef</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to a 3 part series
&lt;/h2&gt;

&lt;p&gt;In mathematics, and more specifically in graph theory, a directed graph is a graph that is made up of a set of vertices (often called nodes) connected by directed edges (often called arcs).&lt;/p&gt;

&lt;p&gt;The directed nature of the graph is useful in many cases because it allows us to precisely describe relationships between any vertices of the graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  You already manipulate Directed Graphs without knowing it
&lt;/h2&gt;

&lt;p&gt;Did you know that you were creating directed graphs whenever an import mechanism is used behind the scenes?&lt;/p&gt;


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

&lt;p&gt;Take for instance the image above with four vertices, each representing a JavaScript file.&lt;/p&gt;

&lt;p&gt;Now the question is: what are the &lt;strong&gt;relationships&lt;/strong&gt; between these files? In all programming languages, one file might import one or multiple files. Whenever a file imports another one, an implicit relationship is created. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;src/hello.js&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;src/main.js&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see above, &lt;strong&gt;main.js&lt;/strong&gt; imports &lt;strong&gt;hello.js&lt;/strong&gt; to use the &lt;code&gt;sayHello&lt;/code&gt; function. The static import creates an implicit relationship between both files.&lt;/p&gt;

&lt;p&gt;In the field of graphs, this relationship can be modeled as a directed edge from &lt;strong&gt;main.js&lt;/strong&gt; to &lt;strong&gt;hello.js&lt;/strong&gt; (can be written as &lt;strong&gt;main.js ---&amp;gt; hello.js&lt;/strong&gt;). We say that main.js &lt;strong&gt;is adjacent&lt;/strong&gt; to hello.js but generally speaking, we can allow ourselves to say that main.js &lt;strong&gt;depends on&lt;/strong&gt; hello.js.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given two vertices A and B, if a directed Edge X starting from A to B we can then say that A is adjacent to B.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can now update the graph with our edges represented:&lt;/p&gt;


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

&lt;p&gt;Basically this graph says that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FileA directly depends on FileD and FileB&lt;/li&gt;
&lt;li&gt;FileA indirectly depends on FileC (through both FileD and FileB)&lt;/li&gt;
&lt;li&gt;FileD directly depends on FileC&lt;/li&gt;
&lt;li&gt;FileB directly depends on FileC&lt;/li&gt;
&lt;li&gt;FileC depends on nothing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure may seem simple but can in fact be used to model very complex schemas. Let's take a look at three examples of interesting use cases for directed graphs.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;&lt;em&gt;Static dependencies analysis&lt;/em&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;=&amp;gt; Cycle dependencies detection by ESLint for JavaScript &lt;a href="https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-cycle.md" rel="noopener noreferrer"&gt;ESLint no-cycle plugin&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Circular dependencies can make your program crash or introduce inconsistencies in many various ways in this is not something you should underestimate. Luckily in Node.js, most famous module systems can resolve dependencies cycles and avoid your program to crash (some module systems do better than others, though). &lt;/p&gt;

&lt;p&gt;Nevertheless, circular dependencies are often an indicator that there is a more or less deep misconceptions in your project, so I always advise to resolve circular dependencies.&lt;/p&gt;

&lt;h4&gt;
  
  
  Further exploring of the circular dependencies detection:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/antoine-coulon/digraph-js/tree/master/examples/circular-dependencies" rel="noopener noreferrer"&gt;See an implementation of a circular dependency detection using the &lt;strong&gt;&lt;code&gt;digraph-js&lt;/code&gt;&lt;/strong&gt; library I wrote&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;&lt;em&gt;Incremental/Affected tasks&lt;/em&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;=&amp;gt; Bundlers/Monorepos tools make extensive use of it (e.g: &lt;a href="https://nx.dev/using-nx/affected" rel="noopener noreferrer"&gt;NX's affected build/test/lint...&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;A directed graph can also be used in order to establish &lt;strong&gt;affected&lt;/strong&gt; patterns. The &lt;strong&gt;affected&lt;/strong&gt; pattern consists in analyzing source code and figuring out what can be affected by every code change.&lt;/p&gt;

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

&lt;p&gt;In the image above, we can see a project using an &lt;strong&gt;affected&lt;/strong&gt; strategy to build only what really needed to be rebuilt. When building the &lt;strong&gt;Main App&lt;/strong&gt; after &lt;strong&gt;Library 2&lt;/strong&gt; changed, only &lt;strong&gt;Library 1&lt;/strong&gt; and &lt;strong&gt;Library 2&lt;/strong&gt; must be rebuilt. The other part of the graph (&lt;strong&gt;Library 3&lt;/strong&gt;, &lt;strong&gt;Library 4&lt;/strong&gt;, &lt;strong&gt;Library 5&lt;/strong&gt;) remains unaffected hence the cached version of these libraries can be used in the final build (no need to rebuild them).&lt;/p&gt;

&lt;p&gt;In a monorepo or in custom projects setup, this &lt;strong&gt;affected&lt;/strong&gt; pattern can drastically reduce the time taken by trivial tasks such as &lt;strong&gt;build/test/lint&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Further exploring of the Affected pattern:
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/antoine-coulon/digraph-js/tree/master/examples/affected-builds" rel="noopener noreferrer"&gt;See an implementation of an affected pattern using the &lt;strong&gt;&lt;code&gt;digraph-js&lt;/code&gt;&lt;/strong&gt; library I wrote&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;&lt;em&gt;Task orchestration&lt;/em&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In the context of task orchestration/scheduling, directed graphs can also be very precious.&lt;/p&gt;

&lt;p&gt;Take for instance a tool that everybody knows: Microsoft Excel. Have you ever wondered how changing the formula from one cell could directly affect other cells depending on this formula? I hope you are not disappointed to learn that it is not an infinite loop running under the hood.&lt;/p&gt;

&lt;p&gt;In this context, our Directed Graph has a vertex for each cell to be updated and an edge in between whenever one of them needs to be updated earlier than the other. &lt;/p&gt;

&lt;p&gt;Due to the fact that we need to consistently schedule the tasks involved, we can't have cycles in our Graph. Dependency Graphs without circular dependencies form &lt;strong&gt;&lt;em&gt;Directed Acyclic Graphs (DAGs)&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;acyclic&lt;/strong&gt; constraint allows us to be consistent with the order of the various operations involved by updates.&lt;/p&gt;

&lt;p&gt;In another context of Task Orchestration, we can briefly talk about Task Scheduling, which includes sequential vs parallel patterns.&lt;/p&gt;

&lt;p&gt;Thanks to DAGs, we can easily determine what tasks can be run in parallel (no common dependencies in the Graph) and what tasks must be run sequentially (one after the other because one depends on the other).&lt;/p&gt;

&lt;p&gt;Similar problems of Task Ordering arise in makefiles for program compilation, in YAML files for CI/CD and instruction scheduling for low-level computer program optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay tuned
&lt;/h2&gt;

&lt;p&gt;I'm planning to showcase some use cases introducing the use of graphs using the &lt;a href="https://github.com/antoine-coulon/digraph-js" rel="noopener noreferrer"&gt;digraph-js&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/antoine-coulon/digraph-js/tree/master/examples" rel="noopener noreferrer"&gt;Some examples are already available on my GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading :)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>tutorial</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>🔒 Make your JavaScript project safer by using this workflow</title>
      <dc:creator>Antoine Coulon</dc:creator>
      <pubDate>Tue, 01 Feb 2022 18:41:03 +0000</pubDate>
      <link>https://dev.to/nodesecure/make-your-javascript-project-safer-by-using-this-workflow-403a</link>
      <guid>https://dev.to/nodesecure/make-your-javascript-project-safer-by-using-this-workflow-403a</guid>
      <description>&lt;h2&gt;
  
  
  The security issue
&lt;/h2&gt;

&lt;p&gt;Have you ever been thinking about security in your JavaScript projects? No? Well, you should, because with &lt;strong&gt;new &lt;em&gt;thousands of package&lt;/em&gt; published on &lt;code&gt;npm&lt;/code&gt; everyday&lt;/strong&gt;, vulnerabilities could come from your own code but also from your direct dependencies (node_modules).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Few months ago, &lt;strong&gt;coa&lt;/strong&gt; npm library was used to steal users' personal data by injecting malicious code.&lt;br&gt;
As a reminder &lt;strong&gt;coa&lt;/strong&gt; was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloaded approximately 9 million times per week&lt;/li&gt;
&lt;li&gt;Used by about 5 million GitHub projects&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's just one story among many others...&lt;/p&gt;

&lt;p&gt;If you're using &lt;code&gt;npm&lt;/code&gt; to download dependencies, you have probably already encountered this message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jg2J2EXA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6p39tsqv6avb0jm2fac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jg2J2EXA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6p39tsqv6avb0jm2fac.png" alt="npm audit" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After each &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt; runs an audit scan against your updated dependencies. Here, we have 79 vulnerabilities, coming from one or many dependencies. Each one represents a potential threat and should be fixed.&lt;/p&gt;

&lt;p&gt;Where do these vulnerabilities come from? Basically, &lt;code&gt;npm&lt;/code&gt; maintains a vulnerability Database which is updated on a daily basis. Many other databases exist, here is an exhaustive list about  most popular open-source databases for the JavaScript ecosystem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nodejs/security-wg"&gt;Node.js Security Working Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://security.snyk.io/vulns?type=npm"&gt;Snyk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/advisories?query=type%3Areviewed+ecosystem%3Anpm"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These ressources are great, but we are lazy developers focused on productivity and we want to automate that, so we don't have to manually check all databases at 8 am every day before processing new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The security solution
&lt;/h2&gt;

&lt;p&gt;First things first, I want to warn you about the fact that there is no silver bullet for security concerns. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If security were all that mattered, computers would never be turned on, let alone hooked into a network with literally millions of potential intruders. &lt;em&gt;&lt;strong&gt;Dan Farmer&lt;/strong&gt;, pioneer in the development of vulnerability scanners for Unix operating systems and computer networks&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nevertheless, you can drastically reduce the amount of vulnerabilities by using tools that can be easily integrated with your projects.&lt;br&gt;
However most of the time these tools are not open-source hence not for free use.&lt;/p&gt;
&lt;h3&gt;
  
  
  NodeSecure Continuous Integration
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;NodeSecure is an open source organization that aims to create free JavaScript security tools. Our biggest area of expertise is in npm package and code analysis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To see more, read these &lt;a href="https://dev.to/fraxken/announcing-new-node-secure-back-end-1dp9"&gt;NodeSecure series&lt;/a&gt;, written by &lt;a href="https://github.com/fraxken"&gt;Thomas @fraxken&lt;/a&gt;, founder of the GitHub organization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is @nodesecure/ci&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/NodeSecure/ci"&gt;@nodesecure/ci&lt;/a&gt; brings together a set of tools to identify dependencies vulnerabilities and track most common malicious code and patterns using &lt;strong&gt;Static Code Analysis&lt;/strong&gt; and &lt;strong&gt;Vulnerabilities Analysis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your project (custom configuration is available) passes all security checks, the process exit with no error code otherwise, it fails.&lt;/p&gt;

&lt;p&gt;Here is a preview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nsGl64Xr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a22zcc5arftcg51z0iwi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nsGl64Xr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a22zcc5arftcg51z0iwi.gif" alt="NodeSecure Continuous Integration preview" width="600" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- GitHub Action&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you use &lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt;, you have a very straightforward way to add the official &lt;a href="https://github.com/NodeSecure/ci-action"&gt;NodeSecure ci-action&lt;/a&gt; action to your workflow: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;workflow.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodeSecure/ci-action@v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your source code and its dependencies will be automatically analyzed, &lt;strong&gt;&lt;em&gt;ironically without even adding new dependencies to your projects&lt;/em&gt;&lt;/strong&gt;. That's also a perfect fit if your tech lead doesn't want you to add new dependencies (node_modules already heavier than the universe).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Node.js Script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install the &lt;a href="https://www.npmjs.com/package/@nodesecure/ci"&gt;@nodesecure/ci&lt;/a&gt; package and start using the entry script &lt;code&gt;node_modules/.bin/nsci&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As well as for the GitHub Action, you can provide a custom configuration through CLI arguments.&lt;/p&gt;

&lt;p&gt;First, reference the binary script in the &lt;em&gt;package.json&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"nsci"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nsci"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start it providing different arguments (all can be used at once, by the way):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run nsci &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Users/user1/myproject
&lt;span class="nv"&gt;$ &lt;/span&gt;npm run nsci &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;npm
&lt;span class="nv"&gt;$ &lt;/span&gt;npm run nsci &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--vulnerability&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;all
&lt;span class="nv"&gt;$ &lt;/span&gt;npm run nsci &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--warnings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;error
&lt;span class="nv"&gt;$ &lt;/span&gt;npm run nsci &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--reporters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;console
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;- Module API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;@nodesecure/ci exposes its pipeline runner as an API to allow use in any other combined workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nx"&gt;runPipeline&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@nodesecure/ci&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optionsExample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;vulnerabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reporters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;console&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;runPipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;optionsExample&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; the process can either exit with error code (1) &lt;/span&gt;
&lt;span class="c1"&gt;// or no error code (0), depending on the pipeline status.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, now you have no more excuses not to practice &lt;a href="https://www.redhat.com/en/topics/devops/what-is-devsecops?sc_cid=7013a000002pz9ZAAQ&amp;amp;gclid=Cj0KCQiA0eOPBhCGARIsAFIwTs6FBfp7l54w_KoGuBSZ1pKqwugf9tPF-WMI-K71NtQT-l__HYtgN6saAlgWEALw_wcB&amp;amp;gclsrc=aw.ds"&gt;DevSecOps&lt;/a&gt; =) &lt;/p&gt;

&lt;p&gt;Any feedback on &lt;a href="https://github.com/NodeSecure/ci"&gt;@nodesecure/ci&lt;/a&gt; is welcome, the library is just getting started.&lt;/p&gt;

&lt;p&gt;Feel free to reach me on GitHub @antoine-coulon&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>security</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
