<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Adrian Jiga</title>
    <description>The latest articles on Forem by Adrian Jiga (@adrianjiga).</description>
    <link>https://forem.com/adrianjiga</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%2F3139217%2Ffd68cc18-bc91-4d68-9c10-ee3a4c88d3ab.jpeg</url>
      <title>Forem: Adrian Jiga</title>
      <link>https://forem.com/adrianjiga</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/adrianjiga"/>
    <language>en</language>
    <item>
      <title>6 Things About The Terminal That Confuse Everyone (And Nobody Warns You About)</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Mon, 30 Mar 2026 19:22:18 +0000</pubDate>
      <link>https://forem.com/adrianjiga/6-things-about-the-terminal-that-confuse-everyone-and-nobody-warns-you-about-4kd2</link>
      <guid>https://forem.com/adrianjiga/6-things-about-the-terminal-that-confuse-everyone-and-nobody-warns-you-about-4kd2</guid>
      <description>&lt;p&gt;You open a terminal for the first time in and stare at a black screen with a blinking cursor. You type something. You press Enter. Nothing happens. No confirmation, no progress bar, no “Done!”, just the cursor blinking back at you like it’s waiting for something else.&lt;/p&gt;

&lt;p&gt;So you type it again. Still nothing. You close the terminal and go back to clicking around in a GUI.&lt;/p&gt;

&lt;p&gt;That’s how most people’s first experience with the terminal ends. Not because the command was wrong. Because the terminal was working exactly as intended, and nobody explained how it actually behaves before handing it to you.&lt;/p&gt;

&lt;p&gt;There’s a concept in psychology called the curse of knowledge. Once you know something well enough, you lose the ability to remember what it felt like not to know it. The thing that was once confusing becomes invisible, not because it stopped being confusing, but because your brain quietly filed it away and moved on.&lt;/p&gt;

&lt;p&gt;Terminal guides are written almost entirely by people who are deep under the curse of knowledge. They know what the &lt;code&gt;$&lt;/code&gt; symbol means. They know why the screen goes blank after a successful command. They know what happens when you press &lt;code&gt;Ctrl+C&lt;/code&gt;. None of it registers as something that needs explaining anymore so they skip straight to the commands, and beginners hit a wall they never saw coming.&lt;/p&gt;

&lt;p&gt;This isn’t a guide about commands. It’s about the six things that will confuse you before you even get to commands, the behaviors that feel broken until you understand why they exist.&lt;/p&gt;

&lt;p&gt;These are the six that get everyone.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. When it says nothing, that means it worked
&lt;/h2&gt;

&lt;p&gt;Coming from graphical interfaces, you’re trained to expect feedback. You save a file, the title bar updates. You install software, a progress bar fills. Something always confirms that the thing happened.&lt;/p&gt;

&lt;p&gt;The terminal doesn’t work like that.&lt;/p&gt;

&lt;p&gt;When a command succeeds, you get your prompt back. That’s it. No checkmark, no “Success!”, no output of any kind. Just the cursor, ready for the next 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;cp &lt;/span&gt;cypress/fixtures/user.json cypress/fixtures/user.backup.json
&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That blank line isn’t the terminal ignoring you. The file was copied. The terminal is done. It moved on.&lt;/p&gt;

&lt;p&gt;This is a deliberate design choice, not an oversight. The philosophy is that output means something went wrong, or you specifically asked for information. Silence means everything went fine. Once you internalize that, it actually feels clean, you’re only reading output when there’s something worth reading.&lt;/p&gt;

&lt;p&gt;But until it clicks, every successful command feels like a failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Your password is invisible and that’s intentional
&lt;/h2&gt;

&lt;p&gt;The first time you’re asked to enter a password in the terminal, you type it and watch nothing appear. Not dots, not asterisks. Nothing. The cursor just sits there.&lt;/p&gt;

&lt;p&gt;The natural assumption is that the keyboard has stopped working. It hasn’t.&lt;/p&gt;

&lt;p&gt;The terminal hides password input completely, not even placeholder characters, to prevent what’s called shoulder surfing. If someone’s watching your screen, they can’t tell how many characters your password is, which gives them nothing useful. Even the length of your password stays hidden.&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;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-Syu&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; password &lt;span class="k"&gt;for &lt;/span&gt;adrian:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type your password and press Enter. It’s being captured. The blank input is the feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The &lt;code&gt;$&lt;/code&gt; in tutorials is not part of the command
&lt;/h2&gt;

&lt;p&gt;Every terminal guide, every Stack Overflow answer, every documentation page shows commands like this:&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;cd &lt;/span&gt;Documents
&lt;span class="nv"&gt;$ &lt;/span&gt;npx cypress run
&lt;span class="nv"&gt;$ &lt;/span&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;$&lt;/code&gt; is the prompt symbol. It’s what the terminal prints to indicate it’s ready for input, it’s marking where your command starts, not contributing to it. When you see &lt;code&gt;$ npm install&lt;/code&gt;, you type &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You’ll also see &lt;code&gt;%&lt;/code&gt; on Zsh, &lt;code&gt;#&lt;/code&gt; when running as root, and sometimes no symbol at all. The character varies, but the role is always the same: it’s a separator between the terminal’s state and your input.&lt;/p&gt;

&lt;p&gt;Nobody explains this upfront because to anyone who’s been using a terminal for more than a week, it stopped registering as something that needed explaining. But copy-pasting that &lt;code&gt;$&lt;/code&gt; is one of the most common things that breaks a first command.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Ctrl+C does not copy, it kills
&lt;/h2&gt;

&lt;p&gt;In every other context, &lt;code&gt;Ctrl+C&lt;/code&gt; copies selected text. In the terminal, it sends an interrupt signal that stops whatever is currently running.&lt;/p&gt;

&lt;p&gt;If you’re running a test suite, a server, a long install, or anything else that’s actively executing, and you reach for &lt;code&gt;Ctrl+C&lt;/code&gt; trying to copy something, you’ve just cancelled it.&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;npx cypress run
Running: login.cy.js &lt;span class="o"&gt;(&lt;/span&gt;1 of 8&lt;span class="o"&gt;)&lt;/span&gt;

  Login flow
    ✓ loads the login page &lt;span class="o"&gt;(&lt;/span&gt;432ms&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ logs &lt;span class="k"&gt;in &lt;/span&gt;with valid credentials &lt;span class="o"&gt;(&lt;/span&gt;1204ms&lt;span class="o"&gt;)&lt;/span&gt;
    - should redirect after login

^C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;^C&lt;/code&gt; in the output is the terminal’s way of showing you the interrupt happened. Six test files never ran. The process is gone.&lt;/p&gt;

&lt;p&gt;To copy in most terminals, the shortcut is &lt;code&gt;Ctrl+Shift+C&lt;/code&gt;. To paste, &lt;code&gt;Ctrl+Shift+V&lt;/code&gt;. The extra &lt;code&gt;Shift&lt;/code&gt; is the only difference between copying text and killing a process.&lt;/p&gt;

&lt;p&gt;Once you know what it actually does, &lt;code&gt;Ctrl+C&lt;/code&gt; becomes one of the most useful things in the terminal. Stuck in a command that won’t finish? &lt;code&gt;Ctrl+C&lt;/code&gt;. Accidentally started something you didn’t mean to? &lt;code&gt;Ctrl+C&lt;/code&gt;. It’s the universal escape hatch. Just not for copying.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. If you accidentally open Vim, here’s how to get out
&lt;/h2&gt;

&lt;p&gt;At some point running &lt;code&gt;git rebase -i&lt;/code&gt;, forgetting the &lt;code&gt;-m&lt;/code&gt; flag on a commit, or following a tutorial that assumes you know what you’re doing, you will end up inside Vim without meaning to. You’ll know because the screen looks nothing like a normal editor, your keyboard seems to be doing unpredictable things, and there’s no visible way to close it.&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;git commit
&lt;span class="c"&gt;# Vim opens automatically because no -m flag was provided&lt;/span&gt;
&lt;span class="c"&gt;# The screen fills with your diff and a blinking cursor&lt;/span&gt;
&lt;span class="c"&gt;# Nothing you type makes sense&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exit commands:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without saving:&lt;/strong&gt; Press &lt;code&gt;Esc&lt;/code&gt;, type &lt;code&gt;:q!&lt;/code&gt;, press Enter.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Saving and exiting:&lt;/strong&gt; Press &lt;code&gt;Esc&lt;/code&gt;, type &lt;code&gt;:wq&lt;/code&gt;, press Enter.&lt;/p&gt;

&lt;p&gt;That’s all you need for now. The reason the keyboard feels broken is that Vim is a modal editor, it operates in different modes, and the same key does different things depending on which mode you’re in. When you first open it, you’re in normal mode, where most keys are commands rather than characters. Pressing &lt;code&gt;g&lt;/code&gt; doesn’t type the letter &lt;code&gt;g&lt;/code&gt;. It does something else entirely.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Esc&lt;/code&gt; returns you to normal mode from wherever you are. From normal mode, &lt;code&gt;:q!&lt;/code&gt; quits without saving and &lt;code&gt;:wq&lt;/code&gt; saves and quits. You don’t need to understand anything else about Vim to get out of it, and getting out of it is usually all you need.&lt;/p&gt;


&lt;h2&gt;
  
  
  6. Different tutorials show different commands and they’re all correct
&lt;/h2&gt;

&lt;p&gt;You follow a guide that tells you to run one command. You follow another guide and it shows something completely different for the same task. Both claim to be correct. One of them doesn’t work on your machine.&lt;/p&gt;

&lt;p&gt;This isn’t a mistake. It’s because “the terminal” isn’t one thing.&lt;/p&gt;

&lt;p&gt;The terminal is the window. The shell is the program running inside it and the shell is what actually interprets your commands. Different shells have different syntax, different built-in commands, and different defaults. The most common ones you’ll encounter are Bash (the default on most Linux systems and older Macs), Zsh (the default on macOS since Catalina), and Fish (a friendlier alternative some developers prefer).&lt;/p&gt;

&lt;p&gt;Most of the time the commands are identical across shells. But not always. Array syntax, certain string operations, some scripting features, these behave differently depending on which shell you’re in. A script written for Bash won’t necessarily run correctly in Fish.&lt;/p&gt;

&lt;p&gt;On top of that, the operating system matters. A guide written for Ubuntu will use &lt;code&gt;apt&lt;/code&gt;. The same guide for Arch-based systems uses &lt;code&gt;pacman&lt;/code&gt;. On macOS you’d reach for &lt;code&gt;brew&lt;/code&gt;. On Windows, you’re likely in PowerShell or WSL, and neither of those behaves like a Unix shell by default.&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;# Ubuntu / Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git

&lt;span class="c"&gt;# Arch / CachyOS / Manjaro&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; git

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;git

&lt;span class="c"&gt;# These do the same thing. Different OS, different package manager, different command.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a command isn’t working and you’ve typed it correctly, the first thing to check is whether the guide was written for your system. The second thing to check is which shell you’re actually running. You can find out with &lt;code&gt;echo $SHELL&lt;/code&gt;. Most of the time, that’s the answer.&lt;/p&gt;




&lt;p&gt;The people writing terminal guides have been using the terminal long enough that these behaviors stopped being noticeable. The invisible password, the silent success, the &lt;code&gt;$&lt;/code&gt; symbol… none of it registers as something that needs explaining anymore. It’s been second nature for so long that the gap between their experience and a first-timer’s isn’t visible to them.&lt;/p&gt;

&lt;p&gt;The result is that beginners hit these moments, feel like they’re missing something obvious, and conclude the terminal is just not for them. That’s not a skill problem. It’s an orientation problem.&lt;/p&gt;

&lt;p&gt;The terminal is an old interface with a lot of implicit conventions that nobody wrote down because everyone who used it had learned from someone who already knew. Once you see what those conventions are, the rest of it, the commands, the flags, the piping, all of it, starts making sense a lot faster.&lt;/p&gt;

&lt;p&gt;The six things above aren’t edge cases. They’re the first six walls. Most people hit at least three of them in their first session.&lt;/p&gt;

&lt;p&gt;Now you know they’re coming.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>cli</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>You’ve Been Installing Software the Hard Way</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Fri, 27 Mar 2026 17:30:55 +0000</pubDate>
      <link>https://forem.com/adrianjiga/youve-been-installing-software-the-hard-way-1ck4</link>
      <guid>https://forem.com/adrianjiga/youve-been-installing-software-the-hard-way-1ck4</guid>
      <description>&lt;p&gt;Before I used Linux, I installed software the way most people do. You open a browser, search for the thing you want, find the official website, download an installer, click through a wizard, and eventually the software appears on your system. Sometimes it takes five minutes. Sometimes you download something from the wrong site and end up with a toolbar you didn’t ask for. Sometimes the installer asks you to restart your computer to install a text editor.&lt;/p&gt;

&lt;p&gt;It works. But it doesn’t scale, and it doesn’t age well. After a while you have no idea what’s installed on your machine, where it came from, or how to update it cleanly. And updating is the part that really shows the cracks. You find out a new version is available because the software told you, you go back to the website, download the new installer, click through the wizard again, and hope it cleans up after itself properly.&lt;/p&gt;

&lt;p&gt;Package managers solve this. And once you’ve used one properly, going back to the old way feels like a step backward.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a package manager, actually?
&lt;/h2&gt;

&lt;p&gt;A package manager is a tool that handles installing, updating, and removing software on your system. Instead of you hunting down the right installer for the right version on the right website, you just tell the package manager what you want, and it handles the rest. It knows where to get the software, what version to grab, and what other software it needs to work.&lt;/p&gt;

&lt;p&gt;That last part is important. Most software depends on other software. A video editor might need a specific audio library. A web framework might need a particular runtime version. A database client might need a specific version of a driver that itself needs a specific version of something else. When you install things manually, you’re responsible for tracking all of that yourself, and it’s exactly as painful as it sounds.&lt;/p&gt;

&lt;p&gt;A package manager does it automatically. You ask for one thing, it figures out the entire chain of dependencies, and installs everything in the right order.&lt;/p&gt;

&lt;p&gt;Think of it like ordering from a restaurant versus going to three different grocery stores to buy ingredients, then cooking everything from scratch, then figuring out mid-recipe that you’re missing something. Both approaches get you fed. One is a lot less effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows: the platform that came late to this
&lt;/h2&gt;

&lt;p&gt;For most of its history, Windows didn’t have a built-in package manager. You installed software by downloading executables and running them. Each installer did whatever it wanted: some added themselves to the PATH, some didn’t, some left behind registry entries when you uninstalled, some didn’t. Some bundled additional software you didn’t want and hoped you’d miss the checkbox.&lt;/p&gt;

&lt;p&gt;That changed with &lt;strong&gt;winget&lt;/strong&gt;, which Microsoft released in 2020 and has been steadily improving since. It’s built into modern Windows and lets you install software straight from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;winget&lt;/span&gt; &lt;span class="kd"&gt;install&lt;/span&gt; &lt;span class="kd"&gt;Mozilla&lt;/span&gt;.Firefox
&lt;span class="kd"&gt;winget&lt;/span&gt; &lt;span class="kd"&gt;install&lt;/span&gt; &lt;span class="kd"&gt;Git&lt;/span&gt;.Git
&lt;span class="kd"&gt;winget&lt;/span&gt; &lt;span class="kd"&gt;install&lt;/span&gt; &lt;span class="kd"&gt;Microsoft&lt;/span&gt;.VSCode
&lt;span class="kd"&gt;winget&lt;/span&gt; &lt;span class="kd"&gt;install&lt;/span&gt; &lt;span class="kd"&gt;Spotify&lt;/span&gt;.Spotify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s not as mature as what Linux has had for decades, but it’s a real package manager with a real repository and it works well for common tools. You can even export your installed packages to a file and use it to restore your entire setup on a new machine, something that would have taken an afternoon doing manually before.&lt;/p&gt;

&lt;p&gt;Before winget, the community filled the gap. &lt;strong&gt;Chocolatey&lt;/strong&gt; has been around since 2011 and has a massive package repository. It’s still widely used, especially in corporate environments where IT teams need to automate software deployments across hundreds of machines. &lt;strong&gt;Scoop&lt;/strong&gt; is another popular option, leaning more toward developer tools and command-line utilities, and it installs everything to your user directory without needing admin privileges, useful if you’re on a work machine where you don’t have full admin access.&lt;/p&gt;

&lt;p&gt;If you’re a developer on Windows, any of these will improve your life compared to the download-and-run approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  macOS: Homebrew became the de facto standard
&lt;/h2&gt;

&lt;p&gt;macOS doesn’t ship with a package manager for general software either. Apple gives you the App Store for GUI apps and that’s about it. For developers who need command-line tools, libraries, and utilities, the App Store isn’t the answer.&lt;/p&gt;

&lt;p&gt;The community built one that became so widely adopted it’s essentially assumed to be installed on any developer’s Mac.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Homebrew&lt;/strong&gt; is that tool. You install it with a single command, and then the whole thing just works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;git
brew &lt;span class="nb"&gt;install &lt;/span&gt;node
brew &lt;span class="nb"&gt;install &lt;/span&gt;ffmpeg
brew &lt;span class="nb"&gt;install &lt;/span&gt;wget
brew &lt;span class="nb"&gt;install &lt;/span&gt;imagemagick
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Homebrew calls its packages “formulae” for command-line tools and “casks” for GUI applications. Want to install a desktop app without touching a browser?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; firefox
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; visual-studio-code
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; vlc
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; discord
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It handles updates cleanly too. &lt;code&gt;brew update&lt;/code&gt; refreshes the package list, &lt;code&gt;brew upgrade&lt;/code&gt; upgrades everything outdated. One command and your whole system is current. Compare that to opening five different applications, clicking “check for updates” in each one, and waiting for them to download and restart individually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MacPorts&lt;/strong&gt; is the older alternative that predates Homebrew and some people still prefer it, particularly for certain scientific or research tools. But Homebrew won the adoption battle years ago. If you’re on a Mac, Homebrew is almost certainly already there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linux: this is where it gets interesting
&lt;/h2&gt;

&lt;p&gt;Linux distributions have had package managers since the 1990s. It’s baked into how the whole system works, not bolted on afterward. On Linux, the package manager isn’t a convenience, it’s the primary way you’re expected to interact with software. The entire operating system, including the kernel, the desktop environment, and every tool installed on it, is managed through it.&lt;/p&gt;

&lt;p&gt;That means when you run a system update on Linux, you’re not just updating one application. You’re updating everything at once, in one command, in a consistent and predictable way.&lt;/p&gt;

&lt;p&gt;The package manager you get depends on which distribution you’re using, because Linux distros are built around different packaging formats and different philosophies about how software should be distributed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debian and Ubuntu&lt;/strong&gt; use &lt;code&gt;apt&lt;/code&gt; (Advanced Package Tool). It manages &lt;code&gt;.deb&lt;/code&gt; packages and is probably the most widely recognized Linux package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;vlc
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;htop
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;update&lt;/code&gt; command refreshes the list of available packages. The &lt;code&gt;upgrade&lt;/code&gt; command actually installs newer versions. It’s a two-step process, which trips up newcomers who run &lt;code&gt;apt update&lt;/code&gt; and wonder why nothing changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fedora and Red Hat&lt;/strong&gt; use &lt;code&gt;dnf&lt;/code&gt;, which manages &lt;code&gt;.rpm&lt;/code&gt; packages. The commands are similar enough that switching between them doesn’t take long to get used to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;vlc
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Arch Linux&lt;/strong&gt; (and Arch-based distros like CachyOS, Manjaro, or EndeavourOS) use &lt;code&gt;pacman&lt;/code&gt;. It’s fast, minimal, and does exactly what you ask with no ceremony:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; git
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; vlc
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-Syu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-Syu&lt;/code&gt; flag is the one you’ll type constantly on Arch: it syncs the package database, updates all packages, and upgrades the system in one shot. Rolling release distributions like Arch stay current by design, so running this regularly is just part of the workflow. Think of it like a standing order. Rather than manually restocking your pantry item by item whenever something runs out, you’ve arranged for everything to be automatically topped up at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;openSUSE&lt;/strong&gt; uses &lt;code&gt;zypper&lt;/code&gt;. &lt;strong&gt;Void Linux&lt;/strong&gt; uses &lt;code&gt;xbps&lt;/code&gt;. &lt;strong&gt;Alpine&lt;/strong&gt; uses &lt;code&gt;apk&lt;/code&gt;. Every major Linux distribution has its own tool, but the underlying concept is always the same: a central repository of packages, a tool to interact with it, and automatic dependency resolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AUR: Arch’s unique addition
&lt;/h2&gt;

&lt;p&gt;If you use Arch or an Arch-based distro, you’ll eventually hear about the AUR, the Arch User Repository. It’s worth understanding because it’s unlike anything the other ecosystems have.&lt;/p&gt;

&lt;p&gt;The official Arch repositories contain software that the Arch team maintains and packages. The AUR is a community repository where anyone can submit a PKGBUILD, a script that describes how to download, build, and install a piece of software. It’s not precompiled packages you install directly. It’s instructions that your machine uses to build the software locally from source.&lt;/p&gt;

&lt;p&gt;The result is that practically anything you could want is available on Arch. Obscure tools, proprietary software, niche utilities, things that haven’t made it into official repositories anywhere. If it exists, someone has probably written a PKGBUILD for it. Tools like &lt;code&gt;yay&lt;/code&gt; or &lt;code&gt;paru&lt;/code&gt; let you interact with both the official repos and the AUR with the same syntax, so in practice you barely notice the distinction.&lt;/p&gt;

&lt;p&gt;The AUR is a bit like a community cookbook where anyone can contribute recipes. The official repos are the curated menu, tested, maintained, guaranteed to work. The AUR is the full cookbook, vastly more options, but you’re expected to use some judgment about what you try.&lt;/p&gt;

&lt;h2&gt;
  
  
  BSD: the same idea, different world
&lt;/h2&gt;

&lt;p&gt;BSD systems, FreeBSD, NetBSD, OpenBSD, have their own package managers. FreeBSD uses &lt;code&gt;pkg&lt;/code&gt;, which manages binary packages similar to how apt or pacman works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;git
pkg &lt;span class="nb"&gt;install &lt;/span&gt;vim
pkg upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FreeBSD also has the Ports collection, which is conceptually similar to the AUR: a collection of build scripts for thousands of pieces of software that you compile from source on your own machine. It’s been around since the early 1990s and was actually an inspiration for how Arch’s own packaging system was later designed.&lt;/p&gt;

&lt;p&gt;If you ever find yourself on a BSD system, the commands are a little different but the mental model is exactly the same. Same restaurant, slightly different menu, same way of ordering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language-level package managers
&lt;/h2&gt;

&lt;p&gt;Here’s something worth pointing out: once you start writing code, you’ll use package managers that have nothing to do with your operating system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm&lt;/strong&gt; manages JavaScript packages. &lt;strong&gt;pip&lt;/strong&gt; manages Python packages. &lt;strong&gt;cargo&lt;/strong&gt; manages Rust crates. &lt;strong&gt;composer&lt;/strong&gt; manages PHP dependencies. &lt;strong&gt;gem&lt;/strong&gt; manages Ruby libraries. These all work on the same principle as system package managers, a central registry, version resolution, automatic dependency management, but they operate at the language level rather than the system level.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;npm install&lt;/code&gt; you run inside a project is the same concept as &lt;code&gt;apt install&lt;/code&gt; on your system. The &lt;code&gt;pip install requests&lt;/code&gt; you run in a Python script is the same concept as &lt;code&gt;brew install wget&lt;/code&gt; on your Mac. Same idea, different scope.&lt;/p&gt;

&lt;p&gt;Once you see that pattern, it sticks. Every new package manager you encounter, no matter the language or platform, immediately makes sense. The commands are different, but you already understand what’s happening underneath.&lt;/p&gt;

&lt;p&gt;And that’s really the point of understanding any of this. Not to memorize the flags, but to understand what the tool is actually doing. When you do, the rest follows naturally.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Tried CachyOS and Now I Can't Stop Booting Into It</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Mon, 16 Mar 2026 18:46:42 +0000</pubDate>
      <link>https://forem.com/adrianjiga/i-tried-cachyos-and-now-i-cant-stop-booting-into-it-33c7</link>
      <guid>https://forem.com/adrianjiga/i-tried-cachyos-and-now-i-cant-stop-booting-into-it-33c7</guid>
      <description>&lt;p&gt;I've been a Linux user for a while now. Not in the "&lt;em&gt;I rice my desktop for eight hours and post it on r/unixporn&lt;/em&gt;" way, but in the "&lt;em&gt;I genuinely enjoy understanding what's happening under the hood&lt;/em&gt;" way.&lt;/p&gt;

&lt;p&gt;My main machine is a MacBook Air M2. It handles everything work-related without complaints: Cypress, Docker, the whole stack. But I have a secondary laptop that was just sitting there, waiting for a purpose. I'd cycled through a few distros on it over time, never fully committing to any of them. Recently I decided to give CachyOS a proper shot.&lt;/p&gt;

&lt;p&gt;What I didn't expect was how much I'd actually enjoy using it day to day, well beyond just recording videos.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is CachyOS?
&lt;/h2&gt;

&lt;p&gt;CachyOS is an Arch Linux-based distro with one core obsession: making your system as fast as possible.&lt;/p&gt;

&lt;p&gt;Most Linux distributions are built to run on as many machines as possible: old ones, new ones, everything in between. That's great for compatibility, but it means they leave a lot of performance on the table if you're running modern hardware. Think of it like a car that's been tuned to run on any fuel, even the cheap stuff. It works, but it's not running at its best.&lt;/p&gt;

&lt;p&gt;CachyOS takes a different approach. It targets newer CPUs specifically and compiles its packages to actually take advantage of what your hardware can do. The installer detects your CPU and picks the right optimized build automatically; you don't configure anything, it just handles it. On top of that, it ships with a custom kernel tuned specifically for desktop responsiveness. In plain terms: the system feels snappy. Applications open faster, the desktop stays fluid even when something heavy is running in the background, and that sluggishness you sometimes get on Linux when things are under load is just... less present.&lt;/p&gt;

&lt;p&gt;It's not placebo either. I was skeptical going in, but the difference on my secondary laptop is real and noticeable from the first boot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The installation experience
&lt;/h2&gt;

&lt;p&gt;I want to talk about this because it's one of the things that surprised me most.&lt;/p&gt;

&lt;p&gt;CachyOS uses a graphical installer, which means you don't have to manually partition drives or write config files to get up and running. For an Arch-based distro, that's already a big deal. But what stood out was how many choices it actually gives you during installation without making those choices feel overwhelming.&lt;/p&gt;

&lt;p&gt;You pick your desktop environment; there are over 17 options, including KDE Plasma, GNOME, Hyprland, and a bunch of tiling window managers if that's your thing. You pick your filesystem. You pick your shell. I went with KDE Plasma, Btrfs, and fish shell, and the whole process took maybe fifteen minutes.&lt;/p&gt;

&lt;p&gt;One thing I genuinely appreciated: the installer automatically sets up Btrfs snapshots if you choose Btrfs as your filesystem. That means before every system update, a snapshot is taken automatically. If something breaks, you can roll back from the boot menu. For a rolling release distro where you're always on the latest everything, that safety net is really reassuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's it actually like to use?
&lt;/h2&gt;

&lt;p&gt;KDE Plasma 6.6 on CachyOS feels smooth in a way this laptop hasn't before. Wayland works out of the box, no weird flickering, no scaling issues, no "this works but that doesn't." It just works, which honestly still surprises me every time because Wayland on Linux has historically been a "your mileage may vary" situation.&lt;/p&gt;

&lt;p&gt;The terminal is great from the first launch. Fish shell is pre-configured and actually styled nicely, it has syntax highlighting, autosuggestions, and a clean prompt without you having to touch a single config file. In Bash or Zsh you’d have to manually edit the config files to get the same experience. &lt;/p&gt;

&lt;p&gt;There's a welcome app called CachyOS-Hello that opens on first boot and it's genuinely useful, not just a launcher for a wiki. It gives you access to system tweaks, a package installer, and kernel management, all in one place. I spent about twenty minutes in there on the first boot just exploring what was available and enabling a few things.&lt;/p&gt;

&lt;p&gt;The package installer itself deserves a mention. It's a GUI that covers basically everything you'd install with pacman, and because CachyOS is Arch-based, the AUR is fully available too. You can do everything through the terminal if you prefer, but having a visual alternative is a nice touch, especially on a machine you're setting up fresh and just want to get apps installed quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The community and transparency
&lt;/h2&gt;

&lt;p&gt;This is something I didn't think I'd care about but ended up appreciating quite a bit.&lt;/p&gt;

&lt;p&gt;CachyOS has a &lt;code&gt;packages.cachyos.org&lt;/code&gt; dashboard where you can see every package in their repositories: which ones come directly from upstream Arch, which ones have been modified, and which PKGBUILDs are custom to CachyOS. You can see exactly what you're running and why. For a distro that's making a lot of changes under the hood, that kind of transparency matters.&lt;/p&gt;

&lt;p&gt;The community has also grown a lot. The project tripled in size in 2025, and the Discord and forums are active. When I ran into a small issue during setup, I found a forum thread that addressed it exactly within about three minutes of searching. That's the kind of community support that makes a real difference when you're setting up a new system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is this for?
&lt;/h2&gt;

&lt;p&gt;CachyOS isn't a beginner distro, and I want to be upfront about that.&lt;/p&gt;

&lt;p&gt;If you're just getting started with Linux, something like Linux Mint or Manjaro will treat you better. There's more hand-holding, more documentation aimed at newcomers, and a larger safety net when things go wrong. CachyOS is Arch under the hood, which means it's a rolling release and your system is always getting updated to the latest versions of everything. That's exciting, but it also means you should be comfortable reading update notes occasionally and knowing what to do if a package update causes a problem.&lt;/p&gt;

&lt;p&gt;But if you've been using Linux for a while, if you know what a rolling release means, if you've felt the pain of AUR packages breaking on Manjaro because of version mismatches, CachyOS is genuinely worth trying. Because it stays in sync with upstream Arch, the AUR just works. No delays, no compatibility headaches. When a new version of a tool comes out, you get it.&lt;/p&gt;

&lt;p&gt;And if you have a secondary machine where you can experiment without risking your main setup, there's really no reason not to try it. The worst case is you flash a different ISO. The best case is you find a system that actually makes your hardware feel alive.&lt;/p&gt;

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

&lt;p&gt;I came to CachyOS for OBS and content creation. I stayed because the system itself became genuinely fun to use.&lt;/p&gt;

&lt;p&gt;That progression: &lt;strong&gt;secondary laptop gathering dust → CachyOS install → actually looking forward to booting into it&lt;/strong&gt; happened faster than I expected. It's the distro that gave me the full Arch experience without making me earn it the hard way first, and a desktop that finally feels like it's using my hardware properly.&lt;/p&gt;

&lt;p&gt;If you're a Linux person with some experience and a bit of curiosity, give it a shot. Flash the ISO, pick KDE Plasma, and see how your hardware feels when it's not being held back.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>archlinux</category>
      <category>kde</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Fell in Love with Claude Code (And Why You Might Too)</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Wed, 28 Jan 2026 09:14:42 +0000</pubDate>
      <link>https://forem.com/adrianjiga/how-i-fell-in-love-with-claude-code-and-why-you-might-too-5c8a</link>
      <guid>https://forem.com/adrianjiga/how-i-fell-in-love-with-claude-code-and-why-you-might-too-5c8a</guid>
      <description>&lt;p&gt;I've always been curious about AI coding tools. Like most developers, I saw the hype, tried a few chat-based assistants, and found myself spending more time copying and pasting code between windows than actually solving problems. It felt clunky. I kept going back to my old workflow because it was just... faster.&lt;/p&gt;

&lt;p&gt;Then I tried Claude Code, and something changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What even is Claude Code?
&lt;/h2&gt;

&lt;p&gt;Claude Code is a command-line tool from Anthropic. You install it, run &lt;code&gt;claude&lt;/code&gt; in your terminal, and suddenly you have an AI that can actually see your project, read your files, run commands, and make changes directly. No more copy-pasting. No more explaining your folder structure. It just works with what's already there.&lt;/p&gt;

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

&lt;p&gt;The native installer is the recommended way to install Claude Code. It doesn't require Node.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;macOS / Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://claude.ai/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows (PowerShell):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;irm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://claude.ai/install.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternative: npm (if you prefer)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you'd rather use npm, you'll need Node.js 18 or higher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Don't use &lt;code&gt;sudo&lt;/code&gt; with &lt;code&gt;npm install&lt;/code&gt; as it causes permission issues.&lt;/p&gt;

&lt;p&gt;After installation, verify it worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.1.22 (Claude Code)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;Before you can use Claude Code, you need to authenticate. You have two options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Claude Pro or Max subscription (recommended)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have a Claude Pro or Max subscription, just run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It will open your browser for a one-time OAuth login with your Claude account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2: API key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're using the Anthropic API directly, set your key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-api-key-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to your &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt; to make it permanent. Then reload your shell config:&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;# If using bash&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc

&lt;span class="c"&gt;# If using zsh&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Starting your first session
&lt;/h2&gt;

&lt;p&gt;Navigate to any project folder and start Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/my-cypress-tests
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see a welcome screen and a prompt where you can start typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╭─────────────────────────────────────────╮
│ Claude Code                             │
╰─────────────────────────────────────────╯

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

&lt;/div&gt;



&lt;p&gt;Now you can just type what you want Claude to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Explain the structure of this project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will read your files and give you an overview. From here, you can ask follow-up questions, request changes, or have it run commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  My first real use case
&lt;/h2&gt;

&lt;p&gt;I was stuck on a problem that had been eating my afternoon. After upgrading a dependency in one of my projects, a bunch of tests started failing. The error messages weren't helpful, and I couldn't figure out what had changed.&lt;/p&gt;

&lt;p&gt;Out of frustration, I opened Claude Code and just typed something like "these tests broke after I upgraded &lt;code&gt;xmlbuilder2&lt;/code&gt;, can you help me figure out why?"&lt;/p&gt;

&lt;p&gt;What happened next surprised me. It didn't just throw generic suggestions at me. It asked to look at my &lt;code&gt;package.json&lt;/code&gt;, examined the failing tests, checked what changed between versions, and found the issue. A method signature had changed in the new version. It even offered to fix it.&lt;/p&gt;

&lt;p&gt;Two minutes. I'd been stuck for two hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it clicked for me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It understands context.&lt;/strong&gt; This is the big one. When I'm working on my Cypress tests, Claude Code sees my actual project. It knows I'm using Cypress, not Playwright. It sees my folder structure, my custom commands, my fixtures. When it suggests something, it fits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It lives in the terminal.&lt;/strong&gt; So much of my work happens in the terminal anyway: running tests, checking logs, debugging CI pipelines. Having an AI right there, in the same environment, just makes sense. I can run a test, see it fail, and immediately ask "why did this fail?" without switching windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It learns my patterns.&lt;/strong&gt; After working with it on a few files, it picked up how I structure my tests, my naming conventions, how I organize things. When I ask it to write something new, it feels like my code, not generic AI output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some real examples
&lt;/h2&gt;

&lt;p&gt;Here are some actual things I've used it for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging test failures
&lt;/h3&gt;

&lt;p&gt;When a test fails and the error isn't helpful, I can pipe the output directly to Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cypress run &lt;span class="nt"&gt;--spec&lt;/span&gt; &lt;span class="s2"&gt;"cypress/e2e/api/orders.cy.js"&lt;/span&gt; 2&amp;gt;&amp;amp;1 | claude &lt;span class="s2"&gt;"The test on line 45 is failing with 'expected undefined to equal 200'. Can you check why?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads the test output, examines the test file, sees the API response structure, and usually spots the issue, like a response property that got renamed or nested differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating tests from existing patterns
&lt;/h3&gt;

&lt;p&gt;Let's say I have a working test for one integration and need a similar one for another. Instead of copy-pasting and manually changing things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Create a new test file for the Jaggaer integration following the same pattern as cypress/e2e/integrations/coupa.cy.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads the existing test, understands the structure, and generates a new file with the right assertions and setup. I just review and tweak if needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting API tests to Postman
&lt;/h3&gt;

&lt;p&gt;This one saved me a lot of time. I had Cypress tests with all our API calls and the dev team wanted a Postman collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Convert the API calls in cypress/e2e/api/orders.cy.js to a Postman collection JSON file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It extracted all the requests, preserved headers and bodies, and even set up environment variables for things like &lt;code&gt;{{baseUrl}}&lt;/code&gt;. Two minutes instead of an hour.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing CI pipeline issues
&lt;/h3&gt;

&lt;p&gt;GitHub Actions failing with weird bash errors? Just paste the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; My GitHub Action is failing with this error:
&amp;gt; /home/runner/work/_temp/script.sh: line 3: unexpected EOF while looking for matching `"'
&amp;gt; Can you help me fix it?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It explained that my heredoc syntax was being interpreted by the shell and gave me the corrected version with proper quoting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick refactors
&lt;/h3&gt;

&lt;p&gt;Need to rename something across multiple files or update an import pattern?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Rename the function "getOrderData" to "fetchOrderDetails" in all files under cypress/e2e/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It shows you what it's going to change, you confirm, and it's done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful one-liners
&lt;/h2&gt;

&lt;p&gt;You can also run Claude Code with a direct prompt instead of starting an interactive session:&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;# Explain what a file does&lt;/span&gt;
claude &lt;span class="s2"&gt;"explain what cypress/support/commands.js does"&lt;/span&gt;

&lt;span class="c"&gt;# Find potential issues&lt;/span&gt;
claude &lt;span class="s2"&gt;"review cypress/e2e/checkout.cy.js for flaky test patterns"&lt;/span&gt;

&lt;span class="c"&gt;# Generate something quick&lt;/span&gt;
claude &lt;span class="s2"&gt;"add a custom command to cypress/support/commands.js that waits for an API response"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is handy when you just need a quick answer or a single change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slash commands
&lt;/h2&gt;

&lt;p&gt;Inside an interactive session, Claude Code has built-in slash commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/help          - Show all available commands
/clear         - Clear the conversation history
/compact       - Condense conversation to save context
/config        - View or modify settings
/cost          - Show token usage and estimated cost
/doctor        - Check your installation health
/init          - Create a CLAUDE.md file for your project
/review        - Request a code review
/exit          - Exit Claude Code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;/init&lt;/code&gt; command is especially useful. It creates a &lt;code&gt;CLAUDE.md&lt;/code&gt; file in your project that helps Claude understand your codebase, conventions, and preferences.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I appreciate most
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It asks before changing things.&lt;/strong&gt; It shows you what it wants to do and waits for you to say yes. This matters when you're working on real projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It admits when it's not sure.&lt;/strong&gt; I've had it tell me "I'm not certain about this specific behavior—want me to check the docs?" That honesty builds trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It remembers what you're working on.&lt;/strong&gt; Within a session, I can say "actually, apply that same fix to the other files too" and it knows exactly what I mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not magic
&lt;/h2&gt;

&lt;p&gt;I want to be honest here. It's not perfect. Sometimes it suggests things that don't quite work. Sometimes it misunderstands what I'm asking for. Complex refactors still need oversight.&lt;/p&gt;

&lt;p&gt;But it's wrong way less often than I expected. And when it is wrong, explaining why usually gets it to a working solution quickly.&lt;/p&gt;

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

&lt;p&gt;I was skeptical of AI coding tools for a long time. Too much hype, too many demos that didn't hold up on real projects. Claude Code changed how I think about this.&lt;/p&gt;

&lt;p&gt;That progression &lt;strong&gt;skepticism -&amp;gt; curiosity -&amp;gt; trying it on a real problem -&amp;gt; actually relying on it&lt;/strong&gt; happened faster than I expected. It's the tool that took debugging from frustrating to manageable, and repetitive tasks from draining to quick.&lt;/p&gt;

&lt;p&gt;If you've been curious about AI coding assistants but found them clunky, give Claude Code a try. Start with something that's been frustrating you. You might be surprised.&lt;/p&gt;

&lt;h2&gt;
  
  
  More information
&lt;/h2&gt;

&lt;p&gt;You can find more usage patterns and documentation here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.anthropic.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Official Claude Code Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/setup" rel="noopener noreferrer"&gt;Claude Code Setup Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/pricing" rel="noopener noreferrer"&gt;Claude Pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Getting Started with Express.js: Middlewares Explained</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Tue, 02 Sep 2025 06:34:20 +0000</pubDate>
      <link>https://forem.com/adrianjiga/express-middlewares-explained-3f1p</link>
      <guid>https://forem.com/adrianjiga/express-middlewares-explained-3f1p</guid>
      <description>&lt;p&gt;If you’re building with &lt;strong&gt;Express.js&lt;/strong&gt;, you’ve definitely seen the term &lt;strong&gt;middleware&lt;/strong&gt; thrown around. At first, it might seem like an abstract concept, but once you understand it, middleware becomes one of the most powerful parts of Express.&lt;/p&gt;

&lt;p&gt;First, make sure to read part 1 &lt;a href="https://dev.to/adrianjiga/getting-started-with-expressjs-the-basics-1ec8"&gt;here&lt;/a&gt;, where I cover the basics.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk you through what middleware is, how to use it effectively, and some common mistakes you’ll want to avoid. By the end, you’ll know exactly how to structure your middleware so your apps stay clean, efficient, and bug-free.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Middleware in Express?
&lt;/h2&gt;

&lt;p&gt;Middleware is just a &lt;strong&gt;function that runs between the time your server receives a request and when it sends back a response&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it as “in-between” logic. The request comes in → middleware runs → response goes out.  &lt;/p&gt;

&lt;p&gt;In fact, even a basic Express route handler is technically middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users page&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This handler takes in a request (&lt;code&gt;req&lt;/code&gt;), a response (&lt;code&gt;res&lt;/code&gt;), and has the potential to take in &lt;code&gt;next&lt;/code&gt;, which determines whether or not Express should move on to the next piece of middleware. That’s the key idea.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Anatomy of Middleware
&lt;/h2&gt;

&lt;p&gt;A middleware function typically looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Do something with req or res&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;Middleware ran&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Call next() so the next function in line runs&lt;/span&gt;
  &lt;span class="nf"&gt;next&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 three parameters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;req&lt;/code&gt;&lt;/strong&gt; → the incoming request object
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;res&lt;/code&gt;&lt;/strong&gt; → the outgoing response object
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;next&lt;/code&gt;&lt;/strong&gt; → a function that passes control to the next middleware
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don’t call &lt;code&gt;next()&lt;/code&gt;, the chain stops there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Global Middleware with app.use
&lt;/h2&gt;

&lt;p&gt;If you want middleware to run &lt;strong&gt;for every single request&lt;/strong&gt;, you can use &lt;code&gt;app.use&lt;/code&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;logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="s2"&gt;Request URL:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, no matter what route you hit, this logger will run first.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Middleware runs in the order it’s defined. If you place your logger &lt;strong&gt;after&lt;/strong&gt; your routes, it won’t run unless you explicitly call &lt;code&gt;next()&lt;/code&gt; inside those routes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Route-Specific Middleware
&lt;/h2&gt;

&lt;p&gt;Sometimes, you only want middleware to apply to a single route. In that case, you can pass middleware functions directly into your route definitions:&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;auth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nf"&gt;next&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not authorized&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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;User is admin:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users page&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;auth&lt;/code&gt; middleware runs first.
&lt;/li&gt;
&lt;li&gt;If the query string includes &lt;code&gt;?admin=true&lt;/code&gt;, it calls &lt;code&gt;next()&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Otherwise, it stops the chain and sends back “Not authorized.”
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Passing Data Between Middleware
&lt;/h2&gt;

&lt;p&gt;Notice in the example above, we set &lt;code&gt;req.admin = true&lt;/code&gt;. Since each middleware shares the same &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt;, you can attach custom properties and access them later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome, Admin!&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users page&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Mistake: Forgetting return
&lt;/h2&gt;

&lt;p&gt;One of the most &lt;strong&gt;common bugs&lt;/strong&gt; with middleware is forgetting that &lt;code&gt;next()&lt;/code&gt; doesn’t stop your function, it just calls the next middleware, then comes back and continues running your code.&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;auth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not authorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Still runs!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This causes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Cannot set headers after they are sent to the client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix: use &lt;code&gt;return&lt;/code&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;auth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not authorized&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Middleware Runs Before &lt;strong&gt;and After&lt;/strong&gt; &lt;code&gt;next()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Code after &lt;code&gt;next()&lt;/code&gt; still runs, but only after the next middleware finishes.&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;logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="s2"&gt;Before next()&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;next&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="s2"&gt;After next()&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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;Users page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Done&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before next()
Users page
After next()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to do things before &lt;em&gt;and&lt;/em&gt; after route handlers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Middleware in a Nutshell
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Middleware = a function with &lt;code&gt;(req, res, next)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Runs in order, top to bottom
&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;next()&lt;/code&gt; to continue the chain
&lt;/li&gt;
&lt;li&gt;Attach data to &lt;code&gt;req&lt;/code&gt; or &lt;code&gt;res&lt;/code&gt; to share between middleware
&lt;/li&gt;
&lt;li&gt;Always &lt;code&gt;return&lt;/code&gt; if you don’t want code to continue after &lt;code&gt;next()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Can run before &lt;strong&gt;and&lt;/strong&gt; after the next middleware
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you internalize this, you’ll see middleware everywhere: logging, authentication, error handling, validation, body parsing, CORS, etc.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Middleware is at the heart of Express. It may look like just some functions chained together, but with the right design, you can keep your app &lt;strong&gt;organized, reusable, and powerful&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Next time you’re building an Express app, think about what belongs in middleware (e.g. logging, auth, validation, error handling), and your code will thank you.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>express</category>
      <category>programming</category>
    </item>
    <item>
      <title>Getting Started with Express.js: The Basics</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Thu, 28 Aug 2025 19:27:57 +0000</pubDate>
      <link>https://forem.com/adrianjiga/getting-started-with-expressjs-the-basics-1ec8</link>
      <guid>https://forem.com/adrianjiga/getting-started-with-expressjs-the-basics-1ec8</guid>
      <description>&lt;p&gt;When you first dive into backend development with &lt;strong&gt;Node.js&lt;/strong&gt;, it doesn’t take long before you bump into &lt;strong&gt;Express.js&lt;/strong&gt;. It’s practically the default framework for building web servers and APIs in JavaScript, and for good reason, it simplifies the otherwise clunky task of handling requests, responses, and routes.&lt;/p&gt;

&lt;p&gt;In this article, we’ll take a thorough look at Express:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what it is and why it exists,
&lt;/li&gt;
&lt;li&gt;how to set up a simple project,
&lt;/li&gt;
&lt;li&gt;how routing works in practice,
&lt;/li&gt;
&lt;li&gt;and a few must-know tricks to get you comfortable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will lay the foundation for the next article in the series (Part 2), where we’ll unlock the real magic of Express: &lt;strong&gt;middleware&lt;/strong&gt;.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Why Use Express.js Instead of Plain Node?
&lt;/h2&gt;

&lt;p&gt;You might be thinking: &lt;em&gt;“Wait, Node.js can already create servers, why do I need Express?”&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;That’s true, but here’s what a basic raw Node server looks like:&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;http&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;http&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&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;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Home Page&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;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&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;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users Page&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&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;Content-Type&lt;/span&gt;&lt;span class="dl"&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;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not Found&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="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&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="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;Server running on http://localhost:3000&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works, but it gets messy &lt;strong&gt;fast&lt;/strong&gt;. You have to manually check URLs, methods, and headers.  &lt;/p&gt;

&lt;p&gt;Now look at the equivalent in Express:&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;express&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;express&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Home Page&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users Page&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&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="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;Server running on http://localhost:3000&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much cleaner. Express handles the boilerplate for you so you can focus on logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up Express
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a project folder and initialize it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;express-intro
&lt;span class="nb"&gt;cd &lt;/span&gt;express-intro
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install Express:
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a file called &lt;code&gt;server.js&lt;/code&gt; and add:
&lt;/li&gt;
&lt;/ul&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;express&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;express&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// A simple route&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from Express!&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="c1"&gt;// Start the server&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&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="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;Listening on http://localhost:3000&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the server:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to &lt;code&gt;http://localhost:3000&lt;/code&gt; and boom, you’ve got a web server.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Routing Works in Express
&lt;/h2&gt;

&lt;p&gt;Express gives you an intuitive way to define &lt;strong&gt;routes&lt;/strong&gt;. A route is just a combination of an HTTP method and a path.&lt;/p&gt;

&lt;h3&gt;
  
  
  GET Requests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;About page&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visiting &lt;code&gt;http://localhost:3000/about&lt;/code&gt; will trigger this function.&lt;/p&gt;

&lt;h3&gt;
  
  
  POST Requests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/submit&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Form submitted&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would respond when you submit a form with &lt;code&gt;POST /submit&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Routes
&lt;/h3&gt;

&lt;p&gt;You can use route parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users/:id&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`User ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visiting &lt;code&gt;http://localhost:3000/users/42&lt;/code&gt; would return &lt;code&gt;User ID: 42&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query Parameters
&lt;/h3&gt;

&lt;p&gt;Express makes query strings easy too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/search&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`You searched for: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;term&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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visiting &lt;code&gt;http://localhost:3000/search?q=node&lt;/code&gt; responds with &lt;code&gt;You searched for: node&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Serving Static Files
&lt;/h2&gt;

&lt;p&gt;What if you want to serve an HTML file or an image? Express has you covered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&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;Now any files in your &lt;code&gt;public/&lt;/code&gt; folder (like &lt;code&gt;index.html&lt;/code&gt; or &lt;code&gt;style.css&lt;/code&gt;) will be automatically served.&lt;br&gt;&lt;br&gt;
For example: &lt;code&gt;http://localhost:3000/style.css&lt;/code&gt; → serves &lt;code&gt;/public/style.css&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Sending JSON Responses
&lt;/h2&gt;

&lt;p&gt;Since APIs often need to send JSON, Express makes this trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/users&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&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;id&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bob&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visiting &lt;code&gt;/api/users&lt;/code&gt; will give you structured JSON, no extra setup required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Handling 404 Errors
&lt;/h2&gt;

&lt;p&gt;It’s a good practice to handle unknown routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sorry, page not found&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will catch any route that wasn’t matched earlier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Express Feels So Flexible
&lt;/h2&gt;

&lt;p&gt;The magic of Express is its simplicity. It doesn’t force you into a strict structure. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with just a single file for a prototype.
&lt;/li&gt;
&lt;li&gt;Gradually break routes into separate files as your app grows.
&lt;/li&gt;
&lt;li&gt;Add middlewares for logging, authentication, validation, and more (coming in Part 2).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s like Lego blocks: small and simple by themselves, but powerful when combined.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next: Middleware
&lt;/h2&gt;

&lt;p&gt;So far, we’ve built an Express app that can:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;serve routes,
&lt;/li&gt;
&lt;li&gt;return text, HTML, or JSON,
&lt;/li&gt;
&lt;li&gt;handle query strings and parameters,
&lt;/li&gt;
&lt;li&gt;and even serve static files.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But every real-world app has “stuff” that needs to happen before (or after) a request hits your route handler: logging, authentication, input validation, error handling.  &lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;middlewares&lt;/strong&gt; come in.  &lt;/p&gt;

&lt;p&gt;In Part 2, we’ll break down middlewares in detail how they works, how to write your own, and how to avoid common mistakes. That’s when Express really starts to shine.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Daily Habit That Sets Apart Great Engineers</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Sun, 24 Aug 2025 12:53:36 +0000</pubDate>
      <link>https://forem.com/adrianjiga/the-daily-habit-that-sets-apart-great-engineers-1e91</link>
      <guid>https://forem.com/adrianjiga/the-daily-habit-that-sets-apart-great-engineers-1e91</guid>
      <description>&lt;p&gt;After a decade of working alongside engineers at different career stages, I’ve noticed something striking: the most adaptable and innovative engineers share a common trait that goes beyond raw technical skill or natural talent.&lt;/p&gt;

&lt;p&gt;It’s not about being the smartest person in the room or having the perfect educational background. It’s about cultivating a deliberate practice of &lt;strong&gt;continuous learning&lt;/strong&gt; that extends far beyond formal education.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Learning Divide
&lt;/h2&gt;

&lt;p&gt;While average engineers often plateau after mastering their initial tech stack, exceptional ones treat learning as an ongoing process.&lt;/p&gt;

&lt;p&gt;This isn’t about learning for learning’s sake. It’s strategic curiosity that compounds over time, creating engineers who can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate technology shifts without panic&lt;/li&gt;
&lt;li&gt;See patterns across different domains&lt;/li&gt;
&lt;li&gt;Contribute meaningfully to architectural decisions&lt;/li&gt;
&lt;li&gt;Mentor others effectively&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the Habit Forms
&lt;/h2&gt;

&lt;p&gt;The path to continuous learning isn’t always intentional. I’ve observed several common catalysts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Necessity-driven learning&lt;/strong&gt;: Project pivots or role changes force rapid skill acquisition. Engineers who embrace this challenge rather than resist it often discover they enjoy the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Curiosity-driven exploration&lt;/strong&gt;: Some engineers naturally gravitate toward understanding how things work under the hood, leading them down fascinating technical rabbit holes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Community involvement&lt;/strong&gt;: Contributing to open source projects or engaging with technical communities creates a feedback loop of learning and teaching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-functional exposure&lt;/strong&gt;: Working closely with product, design, or data teams broadens perspective and reveals new problem-solving approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Learning Sustainable
&lt;/h2&gt;

&lt;p&gt;The key isn’t cramming more study time into already packed schedules. It’s about building learning into your existing workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn through work&lt;/strong&gt;: Choose projects that stretch your skills slightly. Volunteer for initiatives using unfamiliar technologies. Document what you discover for future reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embrace productive struggle&lt;/strong&gt;: When you encounter something confusing, resist the urge to immediately ask for help. Spend 15-20 minutes trying to figure it out first as this builds problem-solving muscle memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teach to learn&lt;/strong&gt;: Explaining concepts to colleagues, writing internal documentation, or giving tech talks deepens your understanding and reveals knowledge gaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stay selectively informed&lt;/strong&gt;: Rather than trying to track every new framework, focus on understanding underlying principles and emerging patterns that transcend specific tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compounding Returns
&lt;/h2&gt;

&lt;p&gt;Continuous learning creates advantages that multiply over time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Career resilience&lt;/strong&gt;: When technologies shift or markets change, continuous learners adapt faster and with less stress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enhanced problem-solving&lt;/strong&gt;: Exposure to diverse approaches provides a richer toolkit for tackling complex challenges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Increased influence&lt;/strong&gt;: Engineers who understand multiple domains can bridge communication gaps and drive better technical decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Personal satisfaction&lt;/strong&gt;: Learning new things keeps work engaging and prevents the stagnation that leads to burnout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcoming Common Obstacles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;“I don’t have time”&lt;/strong&gt;: Start with just 15 minutes daily. Read one technical article during lunch. Listen to a podcast during your commute. Small, consistent efforts compound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“I don’t know what to learn&lt;/strong&gt;: Follow your frustrations. What slows you down at work? What decisions do you wish you understood better? Let problems guide your learning priorities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Information overload”&lt;/strong&gt;: Focus on fundamentals over frameworks. Understand networking principles rather than memorizing API endpoints. Learn design patterns rather than chasing the latest JavaScript library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“I learn but forget”&lt;/strong&gt;: Apply new knowledge quickly through side projects or work tasks. Teaching others helps solidify understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Long Game
&lt;/h2&gt;

&lt;p&gt;Continuous learning isn’t about becoming a generalist who knows everything superficially. It’s about developing the confidence and capability to learn anything you need, when you need it.&lt;/p&gt;

&lt;p&gt;The engineers I most admire aren’t necessarily the ones who knew the most at any given moment, they’re the ones who could quickly get up to speed on whatever the situation demanded.&lt;/p&gt;

&lt;p&gt;In a field where change is the only constant this skill becomes a competitive advantage that grows stronger with time.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>coding</category>
    </item>
    <item>
      <title>Why Every JavaScript Developer Should Know npm-run-all (Your Life Gets Easier)</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Fri, 15 Aug 2025 19:48:44 +0000</pubDate>
      <link>https://forem.com/adrianjiga/run-multiple-scripts-with-one-command-using-npm-run-all2-3ja0</link>
      <guid>https://forem.com/adrianjiga/run-multiple-scripts-with-one-command-using-npm-run-all2-3ja0</guid>
      <description>&lt;p&gt;If you want a platform-independent way to run multiple npm scripts with a single command, without shell hacks or editor specific tasks, use &lt;code&gt;npm-run-all2&lt;/code&gt;. It’s a maintained fork of npm-run-all and a drop-in replacement with the same commands: &lt;code&gt;run-p&lt;/code&gt;, &lt;code&gt;run-s&lt;/code&gt;, and &lt;code&gt;npm-run-all&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This guide shows practical examples you can paste into &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why npm-run-all2?
&lt;/h2&gt;

&lt;p&gt;It is cross-platform: works the same on macOS, Linux, and Windows and it has simple shorthands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;run-p&lt;/code&gt; - run scripts in parallel&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run-s&lt;/code&gt; - run scripts in sequence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful flags: labels, glob patterns, race mode, continue-on-error, and max concurrency: &lt;code&gt;--race, --continue-on-error, --max-parallel, --print-label&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;If you've never used the original:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; npm-run-all2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Migrating from &lt;code&gt;npm-run-all&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm remove npm-run-all
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; npm-run-all2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage (in parallel)
&lt;/h2&gt;

&lt;p&gt;Use parallel for independent long-running tasks (e.g., dev servers, watchers). Let's say you have this in your &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"frontend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd frontend &amp;amp;&amp;amp; npm run dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"backend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd backend &amp;amp;&amp;amp; npm run start:dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start:dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p frontend backend"&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;Now, if you run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you will have both the backend and the frontend servers up and running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage (in sequence)
&lt;/h2&gt;

&lt;p&gt;Use sequential when you need to run scripts in sequence (e.g. format &amp;gt; lint &amp;gt; compile). Let's say you have this in your &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier --write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint . --ext .js,.jsx,.ts,.tsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"compile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc -p tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-s format lint compile"&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;Now, if you run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you will have basically executed three commands one after the other: npm run format &amp;gt; npm run lint &amp;gt; npm run compile.&lt;/p&gt;

&lt;h2&gt;
  
  
  More information
&lt;/h2&gt;

&lt;p&gt;You can find more usage patterns and combinations here at these URLs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/bcomnes/npm-run-all2/blob/master/docs/npm-run-all.md" rel="noopener noreferrer"&gt;&lt;code&gt;npm-run-all&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bcomnes/npm-run-all2/blob/master/docs/run-p.md" rel="noopener noreferrer"&gt;&lt;code&gt;run-p&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bcomnes/npm-run-all2/blob/master/docs/run-s.md" rel="noopener noreferrer"&gt;&lt;code&gt;run-s&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>node</category>
    </item>
    <item>
      <title>JavaScript is fun to learn, if you do it properly</title>
      <dc:creator>Adrian Jiga</dc:creator>
      <pubDate>Mon, 11 Aug 2025 13:45:12 +0000</pubDate>
      <link>https://forem.com/adrianjiga/javascript-is-fun-to-learn-if-you-do-it-properly-3ano</link>
      <guid>https://forem.com/adrianjiga/javascript-is-fun-to-learn-if-you-do-it-properly-3ano</guid>
      <description>&lt;p&gt;I began my programming journey in high school, where my “Informatics” teacher taught us Pascal. He was very strict about the rules he set—there was no room for creativity; we could only do exactly what he assigned. In college, we studied Java and C++, which I found overly verbose and, to be honest, difficult to read at the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  QA Automation job
&lt;/h2&gt;

&lt;p&gt;After college, I got a job as a QA Automation Engineer, where I worked with Selenium and Java. Since I never really enjoyed Java (and still don’t), it took me a while to understand how Selenium interacts with it and why it works the way it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Studying web dev
&lt;/h2&gt;

&lt;p&gt;A few years into my career, I decided to learn web development, so I started researching which technologies were the most widely used and which tech stacks were considered the best. Like most beginners in web development, I landed on HTML, CSS, and JavaScript. I was already somewhat familiar with HTML and CSS from my QA Automation work, but JavaScript was new to me. At first, I didn’t think too much about it, just that it looked a bit cleaner than Java, but as I learned more about it and discovered what it could do, I slowly began to fall in love with it.&lt;/p&gt;

&lt;p&gt;Over time, JavaScript became more than just another technology I wanted to “try out”. It became the language I truly connected with. I didn’t dive straight into building complex applications. Instead, I took my time to study the fundamentals: understanding variables, loops, functions, and how the DOM worked. My first small victories came from writing simple scripts that updated elements on an HTML page—changing text, hiding or showing content, adding a bit of interactivity. Seeing those changes happen instantly in the browser felt magical compared to my earlier programming experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  The turning point
&lt;/h2&gt;

&lt;p&gt;Once I felt comfortable with the basics, I wanted to take things further, so I started learning Vue. This opened up a whole new way of thinking about building user interfaces, structuring components, managing state, and writing cleaner, more maintainable code. It was exciting to see how JavaScript could power dynamic applications without drowning me in complexity. From there, I moved on to studying Node.js, which was a turning point. I realized I could now use JavaScript on the back end too, meaning I could handle an entire project using a single language from start to finish.&lt;/p&gt;

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

&lt;p&gt;That gradual progression, &lt;strong&gt;fundamentals -&amp;gt; simple scripts -&amp;gt; Vue -&amp;gt; Node.js&lt;/strong&gt;, allowed me to really understand JavaScript instead of just skimming the surface. It’s the language that took me from being someone who mainly tested software to someone who could build it and that's what I wanted in the end, to build software. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
