<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Tilda Udufo - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Tilda Udufo - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:23:51 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/tildaudufo/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ What Your Auth Library Isn't Telling You About Passwords: Hashing and Salting Explained ]]>
                </title>
                <description>
                    <![CDATA[ Before I started building auth into my own projects, I didn't think too deeply about what was happening to passwords behind the scenes. Like most developers, I installed a library, called a hash funct ]]>
                </description>
                <link>https://www.freecodecamp.org/news/passwords-hashing-and-salting-explained/</link>
                <guid isPermaLink="false">69b310eb93256dfc5303de72</guid>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ passwords ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Hashing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Salting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cryptography ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tilda Udufo ]]>
                </dc:creator>
                <pubDate>Thu, 12 Mar 2026 19:15:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/61e84941-bb32-4029-9d58-39022488d29e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Before I started building auth into my own projects, I didn't think too deeply about what was happening to passwords behind the scenes.</p>
<p>Like most developers, I installed a library, called a hash function, stored the result, and moved on. I see a random string like <code>\(2a11yMMbLgN9uY6J3LhorfU9iu....</code> in my database and assume my user's passwords are unbreakable. I knew it was a hashed password. But what was the <code>\)2a</code>? What was <code>11</code>? And if I couldn't reverse it, how was my app verifying logins at all?</p>
<p>If you've ever used bcrypt, Devise, Django's auth system, or really any authentication library, you've been protected from these details. That's good engineering. But understanding what's actually happening makes you a better developer, and it explains a lot of things that seem confusing or arbitrary until suddenly they don't.</p>
<p>By the end of this article, you'll be able to look at that string and know exactly what every part means.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article is written for developers who have used an auth library before but never looked closely at what it's doing. You don't need a cryptography background. If you've ever hashed a password and moved on, this is for you.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a href="#heading-hashing-vs-encryption">Hashing vs Encryption</a></p>
</li>
<li><p><a href="#heading-why-a-plain-hash-isnt-enough">Why a Plain Hash Isn't Enough</a></p>
</li>
<li><p><a href="#heading-enter-salting">Enter Salting</a></p>
</li>
<li><p><a href="#heading-why-bcrypt-is-slow-and-why-thats-the-point">Why bcrypt Is Slow (and Why That's the Point)</a></p>
</li>
<li><p><a href="#heading-whats-actually-in-your-database">What's Actually in Your Database</a></p>
</li>
<li><p><a href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ol>
<h2 id="heading-hashing-vs-encryption">Hashing vs Encryption</h2>
<p>Most developers use the terms <strong>hashing</strong> and <strong>encryption</strong> interchangeably. They're not the same thing, and the difference matters more than you might think.</p>
<p>Encryption is a two-way process. You take data, encrypt it with a key, and you can decrypt it later using that same key (or a related one). This is useful when you need to retrieve the original value. Storing a credit card number you'll need to charge later, or sending a message that the recipient needs to read.</p>
<p>Hashing is different. It's a one-way process. You put data in, you get a fixed-length string out, and there's no key that lets you reverse it. The original value is gone.</p>
<p>That might sound like a limitation. For passwords, it's actually exactly what you want.</p>
<p>Think about it: when a user logs in, you don't need to know their password. You just need to verify that what they typed matches what they set when they signed up. You can do that entirely with hashes. Hash what they typed, compare it to the stored hash, done. You never need the original.</p>
<p>This is why "forgot password" flows always ask you to set a new password rather than sending you your old one. Yes, sending you your old password over email might be risky but the actual reason is that they genuinely can't retrieve it. If they can email you your original password, that's a red flag. It means they stored it in a way that's reversible, which means it's not properly protected.</p>
<h2 id="heading-why-a-plain-hash-isnt-enough">Why a Plain Hash Isn't Enough</h2>
<p>So if hashing is one-way and irreversible, isn't that enough? Just hash every password before storing it and you're done?</p>
<p>Not quite.</p>
<p>The first problem is <strong>rainbow tables</strong>. A <a href="https://en.wikipedia.org/wiki/Rainbow_table">rainbow table</a> is a precomputed database of hashes for common passwords. An attacker who gets hold of your database doesn't need to reverse the hashes. They just look them up. If your user's password is "password123", its <a href="https://en.wikipedia.org/wiki/SHA-2">SHA-256</a> hash is always the same string, and that string is almost certainly already in a rainbow table somewhere.</p>
<p>The second problem is related. If two users have the same password, they'll have the same hash. So if an attacker cracks one, they've cracked all of them. In a database with thousands of users, that's a significant security risk.</p>
<p>Here's what that looks like in practice:</p>
<pre><code class="language-python">import hashlib

# Two users, same password
password = "password123"

hash_one = hashlib.sha256(password.encode()).hexdigest()
hash_two = hashlib.sha256(password.encode()).hexdigest()

print(hash_one == hash_two)  # True, every single time
</code></pre>
<p>The hash is deterministic. The same input always produces the same output. That's useful for a lot of things, but for passwords it creates a real vulnerability.</p>
<p>A plain hash gets you partway there. But it's not enough on its own.</p>
<h2 id="heading-enter-salting">Enter Salting</h2>
<p>The fix for both problems is something called a <strong>salt</strong>. And, no it's not your regular table salt.</p>
<p>A salt is a random string generated uniquely for each password. Before hashing, you combine the salt with the password, then hash the result.</p>
<pre><code class="language-python">import hashlib
import os

password = "password123"

# Generate a random salt
salt = os.urandom(16).hex()

# Combine salt and password, then hash
salted_password = salt + password
hashed = hashlib.sha256(salted_password.encode()).hexdigest()

print(f"Salt: {salt}")
print(f"Hash: {hashed}")
</code></pre>
<p>Now two users with the same password produce completely different hashes, because their salts are different. And because the salt is random and unique, it can't be precomputed into a rainbow table.</p>
<p>Here's the surprising part: <strong>the salt doesn't need to be secret</strong>. It gets stored alongside the hash in your database, in plain text. That might feel wrong at first. If an attacker has your database, they have the salt too.</p>
<p>But that's fine. The salt's job isn't to be secret. Its job is to make each hash unique so that precomputed tables are useless. An attacker who wants to crack a salted hash has to brute force each password individually, from scratch, using that specific salt. They can't reuse work across users.</p>
<p>That's a meaningful increase in the cost of an attack, even when the salt is visible.</p>
<h2 id="heading-why-bcrypt-is-slow-and-why-thats-the-point">Why bcrypt Is Slow (and Why That's the Point)</h2>
<p>Salting solves the rainbow table problem. But there's still a gap. If an attacker has your database and decides to brute force a password, they can just keep guessing. Hash a candidate password with the stored salt, compare it to the stored hash, repeat. With a fast hashing algorithm like SHA-256, a modern GPU can do billions of these comparisons per second.</p>
<p>That's the problem with using a general-purpose hash function for passwords. Algorithms like SHA-256 and MD5 were designed to be fast. That's great for things like verifying file integrity or generating checksums. For passwords, it's a liability.</p>
<p>This is where bcrypt comes in. <a href="https://en.wikipedia.org/wiki/Bcrypt">bcrypt</a> is a password hashing algorithm designed specifically to be slow. Not broken or inefficient by accident, but deliberately, configured-to-be slow. It has a <strong>cost factor</strong> (sometimes called a work factor) that controls how computationally expensive the hashing operation is.</p>
<pre><code class="language-python">import bcrypt

password = b"password123"

# The cost factor is set here (12 is a common production value)
hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12))

print(hashed)
</code></pre>
<p>Every time you increase the cost factor by 1, the hashing operation takes roughly twice as long. At a cost factor of 12, a single hash might take around 300 milliseconds on your server. That's imperceptible to a user logging in. But for an attacker trying to brute force millions of passwords, it turns a feasible attack into an impractical one.</p>
<p>The other advantage of a configurable cost factor is that you can increase it over time as hardware gets faster. What was slow enough in 2015 might not be slow enough today. bcrypt lets you adapt without changing the algorithm itself.</p>
<h2 id="heading-whats-actually-in-your-database">What's Actually in Your Database</h2>
<p>So far, we've talked about salting and cost factors as separate concepts. Here's the satisfying part: in bcrypt, they're all stored together in a single string. That string sitting in your database contains everything needed to verify a password, and once you know how to read it, it's not mysterious at all.</p>
<p>Here's a typical bcrypt hash:</p>
<pre><code class="language-plaintext">\(2a\)12$yMMbLgN9uY6J3LhorfU9iuLAUwKxyy8w42ubeL4MWy7Fh8B.CH/yO
</code></pre>
<p>Let's break it down:</p>
<ul>
<li><p><code>$2a</code> — the <strong>algorithm version</strong>. This tells your auth library which version of bcrypt was used to generate the hash.</p>
</li>
<li><p><code>$12</code> — the <strong>cost factor</strong>. This is the number we talked about in the previous section. A cost factor of 12 means the hashing operation was run 2¹² times.</p>
</li>
<li><p><code>\(yMMbLgN9uY6J3LhorfU9iu</code> — the <strong>salt</strong>. The first 22 characters after the final <code>\)</code> are the salt, stored right there in plain text alongside the hash. Your auth library reads this back out when verifying a login.</p>
</li>
<li><p><code>LAUwKxyy8w42ubeL4MWy7Fh8B.CH/yO</code> — the <strong>hash</strong> itself. The remaining characters are the actual output of the hashing operation.</p>
</li>
</ul>
<p>When a user logs in, your auth library doesn't need any extra information. It reads the algorithm version, cost factor, and salt directly from the stored string, hashes the login attempt using those same parameters, and compares the result. If they match, the password is correct.</p>
<p>This is why bcrypt verification works even though the salt is never stored separately. It was never separate to begin with.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Next time you see a bcrypt string in your database, you'll know exactly what you're looking at. The algorithm version, the cost factor, the salt, and the hash, all encoded in a single string that your auth library knows how to read.</p>
<p>But the bigger takeaway is this: the libraries we rely on every day aren't magic. They're carefully designed systems built on top of concepts that are worth understanding.</p>
<p>Knowing why bcrypt is slow, why salting works even when the salt is visible, and why fast hash functions like SHA-256 are the wrong tool for passwords makes you a more intentional developer. You'll make better decisions about cost factors, you'll recognise a poorly implemented auth system when you see one, and you'll understand why a data breach where passwords were hashed with MD5 is so much worse than one where bcrypt was used.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A Beginner’s Guide to Graphs — From Google Maps to Chessboards ]]>
                </title>
                <description>
                    <![CDATA[ Most of us use Google Maps without thinking twice. You open the app, check which route has the least traffic, and hit start. Then somewhere along the way – maybe you miss a turn (I do that often) – and Maps calmly recalculates your route, showing you... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-beginners-guide-to-graphs/</link>
                <guid isPermaLink="false">683dc829f7d4772789f06d02</guid>
                
                    <category>
                        <![CDATA[ Graph ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ data structures ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DFS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ BFS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tilda Udufo ]]>
                </dc:creator>
                <pubDate>Mon, 02 Jun 2025 15:50:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748879365710/23d7601e-cde0-489b-a843-97190e58e5c9.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Most of us use Google Maps without thinking twice. You open the app, check which route has the least traffic, and hit start. Then somewhere along the way – maybe you miss a turn (I do that often) – and Maps calmly recalculates your route, showing you a new path that still gets you to your destination.</p>
<p>Behind that seamless rerouting is a graph – not a chart, but a structure of <strong>nodes</strong> (places) and <strong>edges</strong> (roads) that allows Google Maps to calculate the shortest, fastest, or least congested path from point A to point B.</p>
<p>Once you start noticing them, you’ll realize graphs are everywhere. If you’ve ever used:</p>
<ul>
<li><p><strong>Google Maps</strong> to get from one city to another,</p>
</li>
<li><p><strong>LinkedIn</strong> to see how you’re connected to someone,</p>
</li>
<li><p>or <strong>Git</strong> to visualize branches and merges…</p>
</li>
</ul>
<p>…you’ve interacted with a graph.</p>
<p>Graphs are everywhere, in how we plan routes, recommend friends, manage project dependencies, and even predict the possible moves of a knight on a chessboard. But to use them well, we first need to understand how they’re structured and why they’re so useful.</p>
<p>In this article, you’ll learn:</p>
<ul>
<li><p>What a graph is and how it’s used in real-world systems</p>
</li>
<li><p>The different types of graphs and how they’re represented in code</p>
</li>
<li><p>How to traverse graphs using search algorithms</p>
</li>
<li><p>And finally, how a knight’s movement in chess can be modeled using graphs, and solved using traversal techniques</p>
</li>
</ul>
<p>Let’s start by breaking down what graphs are made of and why they show up in so many things you already use.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-graph">What is a Graph?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-types-of-graphs">Types of Graphs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-graphs-are-represented">How Graphs Are Represented</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-graph-traversal">Graph Traversal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-knights-travails-a-real-world-graph-problem">Knight's Travails: A Real-World Graph Problem</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>This article is beginner-friendly, and no prior knowledge of graphs is required. To follow along with the code examples, it helps to have:</p>
<ul>
<li><p>Some familiarity with data structures like stacks and queues.</p>
</li>
<li><p>I use Python for the code snippets, but if you’ve worked with another language, you should be able to follow along easily.</p>
</li>
</ul>
<h2 id="heading-what-is-a-graph">What is a Graph?</h2>
<p>At its core, a graph is a collection of <strong>nodes</strong> (also called vertices) and <strong>edges</strong> – connections that link those nodes together.</p>
<p>If it sounds simple, that’s because it is. The power of graphs isn’t in their complexity, it’s in their flexibility. You can use them to represent almost anything: people, cities, web pages, tasks, game moves, and the relationships between them.</p>
<p>Let’s break it down:</p>
<h3 id="heading-nodes-vertices">Nodes (Vertices)</h3>
<p>Each node is a point in the graph. It might represent:</p>
<ul>
<li><p>A location (like a city on a map)</p>
</li>
<li><p>A person (in a social network)</p>
</li>
<li><p>A page (for example, on the web)</p>
</li>
<li><p>A square (like one on a chessboard)</p>
</li>
</ul>
<h3 id="heading-edges">Edges</h3>
<p>An edge is a connection between two nodes. It could represent:</p>
<ul>
<li><p>A road between two cities</p>
</li>
<li><p>A friendship between two users</p>
</li>
<li><p>A hyperlink between two web pages</p>
</li>
<li><p>A legal knight move between two squares</p>
</li>
</ul>
<p>Edges can have direction (one-way or two-way), weight (like distance or cost), or be simple and unweighted.</p>
<h3 id="heading-graph-visualizer">Graph Visualizer</h3>
<p>Here’s a simple graph:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748363812251/59a945ea-eae0-4792-98bb-3f1d8477dde9.png" alt="Graph with 7 circular nodes labeled 0 through 6. Lines connect various pairs of nodes, forming a network-like structure. Each node is connected to one or more others." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In this image:</p>
<ul>
<li><p>The circles represent nodes (also called vertices)</p>
</li>
<li><p>The lines connecting them are edges, which show that two nodes are related or connected in some way</p>
</li>
</ul>
<p>Each node is connected to one or more other nodes, forming a network of relationships<strong>.</strong></p>
<h2 id="heading-types-of-graphs">Types of Graphs</h2>
<p>Now that we’ve seen what a graph looks like, let’s talk about the different ways graphs can behave.</p>
<p>Graphs can vary in <strong>direction</strong>, <strong>weight</strong>, and <strong>structure</strong>. Understanding these types helps us choose the right graph model for the problem we’re trying to solve.</p>
<h3 id="heading-directed-graphs">Directed Graphs</h3>
<p>In a directed graph (also known as a <strong>digraph</strong>), connections between nodes move in a specific direction. Think of it like a one-way street – if you can drive from Point A to Point B, that doesn't necessarily mean you can drive back the same way.</p>
<p>A good example of this is X (Twitter). If you follow someone on X, it doesn’t automatically mean they follow you back. Your “follow” is a one-way connection, a directed edge from you to them.</p>
<p>This kind of graph is especially useful in situations where relationships are not mutual. On the internet, links between web pages behave the same way. Page A might link to Page B, but Page B might not link back. Similarly, in workflow systems or task pipelines, each step flows into the next in a specific order, and you generally don’t loop back – Step 1 leads to Step 2, and so on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748365204873/4926ff56-af90-438d-8281-8423ffa1bfad.png" alt="An directed graph with six blue nodes (0–5) and edges connecting them." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-undirected-graphs">Undirected Graphs</h3>
<p>An undirected graph represents relationships that go both ways. If there’s a connection between node A and node B, you can travel in either direction.</p>
<p>A common example is a friendship on Facebook. If you’re friends with someone, the connection is mutual by default. It wouldn’t make sense to be “half-friends”.</p>
<p>Undirected graphs are useful when the relationship itself is mutual and symmetrical, like shared devices on a network or road systems where travel is allowed both ways.</p>
<p>The key difference here is that edges are just lines, not arrows – they don’t imply flow or hierarchy, just connection.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748365067259/af75d2bc-91ee-459a-8277-d0b53a039bd3.png" alt="An undirected graph with six red nodes (0–5) and edges connecting them symmetrically, showing bidirectional relationships with no arrows." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-weighted-graphs">Weighted Graphs</h3>
<p>A weighted graph is one where each connection (edge) carries extra information, usually a number representing distance, cost, time, or importance.</p>
<p>Consider how you use Google Maps to get from one location to another. The app doesn’t just look for any route, it looks for the one that takes the <strong>least time</strong>, travels the <strong>shortest distance</strong>, or uses the <strong>least fuel</strong>. All of those are weights.</p>
<p>In a graph like this, two cities might be connected, but one road might take 5 minutes while another takes 20, so the edge between those cities isn’t just a line – it’s a line with a value.</p>
<p>This structure lets us make smarter decisions. We can ask questions like:</p>
<ul>
<li><p>What’s the cheapest way to reach a destination?</p>
</li>
<li><p>Which route avoids the most traffic?</p>
</li>
<li><p>What’s the fastest route from node A to node Z?</p>
</li>
</ul>
<p>If undirected and directed graphs describe who is connected to whom, weighted graphs help us understand how strong, far, or costly those connections are<strong>.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748365667877/bfa51c1c-e17d-4db9-aaf9-5d25d1825af0.png" alt="A weighted undirected graph with nine purple nodes (0–8) and labeled edges showing weights. Edge values vary (e.g., 3, 5, 10), modeling a weighted graph." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-unweighted-graphs">Unweighted Graphs</h3>
<p>In an unweighted graph, all edges are treated equally. A connection either exists, or it doesn’t – there’s no extra value or cost attached.</p>
<p>Think of a group of friends where you only care about whether two people know each other. You’re not trying to measure how close they are or how often they talk, you just want to map the presence or absence of a relationship.</p>
<p>Unweighted graphs are useful for modeling systems where the existence of a connection is more important than any nuance of how strong it is. These are great for representing basic relationships, board game states, or decision trees where each path is considered equally likely or valuable.</p>
<p>In short, unweighted graphs are all about the “yes/no,” not the “how much.”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748365816756/761186c7-befb-4dc9-8059-787aac271e05.png" alt="An unweighted graph with orange nodes (0–8). The nodes are connected without arrows, forming a loosely circular layout." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-cyclic-graphs">Cyclic Graphs</h3>
<p>A cyclic graph is one where it’s possible to loop back to where you started. You can travel through a series of connections and eventually end up at the same node.</p>
<p>If you’ve ever looked at a city map or used a public transit system with circular routes, you’ve seen a cycle in action. You might start at a station, ride the train through several stops, and eventually return to where you began, without ever retracing your exact steps.</p>
<p>Cyclic graphs are especially useful in simulations, games, or real-world systems where loops are part of the design, like control circuits or repeated workflows. They’re a natural fit for any scenario where repetition or return paths matter.</p>
<p>But they can also introduce complexity, especially in algorithms that aren’t meant to handle cycles. Recognizing whether your graph has cycles is often an important first step in choosing the right traversal method.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748366079943/bf3b7675-cee8-442a-a680-e52e9ee85612.png" alt="A green-node cyclic graph with eight nodes (0–7)." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-acyclic-graphs">Acyclic Graphs</h3>
<p>An acyclic graph is the opposite of a cyclic one – there are no loops. Once you start moving through the graph, you can’t return to a node you’ve already visited by following the direction of the edges. They can either be directed or undirected graphs.</p>
<p>Think of task management systems where some tasks depend on others. You can't complete Task C until you’ve finished Task B, and you can't start B until A is done. There’s a natural order, and no looping back<strong>.</strong></p>
<p>Acyclic graphs are common in:</p>
<ul>
<li><p>Scheduling systems</p>
</li>
<li><p>Organizational charts</p>
</li>
<li><p>Tutorial progression systems (like one chapter unlocking the next)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748366703312/412cece1-85f2-4251-a422-776d56f115e7.png" alt="A red-node acyclic graph with eight nodes (0–7)." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-directed-acyclic-graphs">Directed Acyclic Graphs</h3>
<p>A Directed Acyclic Graph or DAG is a type of acyclic graph where every edge has a direction, and you can’t form any cycles. It’s like a roadmap where all roads point forward and never loop.</p>
<p>This structure is incredibly common in computing, especially when you need to track progress<strong>,</strong> dependencies<strong>,</strong> or history<strong>.</strong></p>
<p>In Git, for example, every commit points to one or more parent commits, forming a directed graph of changes. But since commits can’t “revisit” an earlier state, the structure remains acyclic.</p>
<p>In package managers, a library might depend on others, but you can't have a loop. Say library A depends on B, which depends on C, which depends on A again – that would break everything. A DAG ensures that dependencies move forward<strong>,</strong> not in circles<strong>.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748366537100/1110ebc4-33eb-4036-9ead-4a6178e02474.png" alt="A blue-node directed acyclic graph (DAG) with eight nodes (0–7). " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-graphs-are-represented">How Graphs Are Represented</h2>
<p>So far, we’ve explored graphs as concepts and visuals with various types and behaviors. But when it’s time to work with graphs in code, we need a way to represent those connections in a structured format.</p>
<p>There are two common ways to represent a graph:</p>
<ul>
<li><p><strong>Adjacency List</strong></p>
</li>
<li><p><strong>Adjacency Matrix</strong></p>
</li>
</ul>
<p>Each has its strengths, and which one you use depends on the type of graph and what operations you need to perform.</p>
<h3 id="heading-adjacency-list">Adjacency List</h3>
<p>An adjacency list stores a graph as a collection of nodes, where each node maps to a list of its neighbors (nodes it’s connected to). An adjacency list is one of the most intuitive ways to represent a graph. But how you write this list depends on the type of graph you’re working with.</p>
<p><strong>For an undirected graph</strong>, the connection goes both ways, so each edge is written twice – once for each node.</p>
<p><strong>For a directed graph</strong>, each connection only flows one way, so you only write it once, in the direction it points.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748375989397/6589e103-8589-4529-9ede-98d298567c39.png" alt="An undirected graph diagram with six nodes labeled A through F, shown alongside its adjacency list representation. In the graph: nodes are connected with straight lines representing undirected edges. The adjacency list shows each node and its neighbors: A connects to E, F, and C. B connects to E and F. C connects to A, D, and E. D connects to C. E connects to A, B, C, and F. F connects to A, B, and E" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Benefits:</strong></p>
<ul>
<li><p>Memory-efficient for sparse graphs (that is, not all nodes are connected)</p>
</li>
<li><p>Easy to add or remove nodes and edges</p>
</li>
<li><p>Fast to get a node’s neighbors – just read its list</p>
</li>
</ul>
<p><strong>Downsides:</strong></p>
<ul>
<li><p>Slower edge lookup: To check if two nodes are connected, you have to scan a list</p>
</li>
<li><p>Can be inefficient for dense graphs where most nodes connect to many others</p>
</li>
<li><p>Sorting neighbors or accessing them by index is less convenient</p>
</li>
</ul>
<h3 id="heading-adjacency-matrix">Adjacency Matrix</h3>
<p>An adjacency matrix uses a 2D array (or grid) to represent connections between nodes. Each row and column corresponds to a node, and the cell at the intersection tells you whether there’s a connection.</p>
<ul>
<li><p>A value of <code>1</code> means there <strong>is</strong> an edge between the two nodes.</p>
</li>
<li><p>A value of <code>0</code> means an edge doesn’t exist between two nodes.</p>
</li>
</ul>
<p><strong>For an undirected graph</strong>, an edge between <code>i</code> and <code>j</code> means the connection works both ways. So if <code>(i, j)</code> is <code>1</code>, <code>(j, i)</code> is also <code>1</code>. This makes the matrix symmetric, that is, <code>(i, j) == (j, i)</code>.</p>
<p><strong>For a directed graph</strong>, an edge from node <code>i</code> to node <code>j</code> is at position <code>(i, j)</code>. But this doesn’t imply that there's an edge from <code>j</code> to <code>i</code>. So in general, <code>(i, j) ≠ (j, i)</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748378013560/6ecd11fc-bede-4dda-b01e-6f6d65f3bcb6.png" alt="A diagram showing a directed graph and its corresponding adjacency matrix. The adjacency matrix is a 6×6 table labeled with nodes A through F. A cell with a value of 1 indicates a directed edge from the row node to the column node.  Node A has outgoing edges to C, E, and F; Node B points to E and F; Node C connects to D and E; Node E connects to F. Nodes D and F have no outgoing edges." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Benefits:</strong></p>
<ul>
<li><p>Instant edge lookup between any two nodes in <code>O(1)</code> time.</p>
</li>
<li><p>Simple to implement and often used in low-node-count, dense graphs.</p>
</li>
</ul>
<p><strong>Downsides:</strong></p>
<ul>
<li><p>High space complexity – even if the graph has few edges, the matrix still takes up <code>O(V²)</code> space.</p>
</li>
<li><p>Inefficient for sparse graphs, where many cells are unused.</p>
</li>
<li><p>Finding neighbors requires scanning an entire row, which takes <code>O(V)</code> time.</p>
</li>
</ul>
<h2 id="heading-graph-traversal">Graph Traversal</h2>
<p>Once you’ve built a graph using adjacency lists or matrices, the next question is: how do you explore it?</p>
<p>Graph traversal is the process of visiting each node (and its neighbors) in a graph in a specific order. It’s a foundational technique in computer science, used in everything from web crawling to friend suggestions on social media.</p>
<p>Traversal is used when:</p>
<ul>
<li><p>You want to search for a specific value or node.</p>
</li>
<li><p>You want to visit all nodes, for example, to analyze connectivity.</p>
</li>
<li><p>You want to solve puzzles, like mazes or routing problems.</p>
</li>
</ul>
<p>There are two primary ways to traverse a graph: <strong>Depth-First Search (DFS)</strong> and <strong>Breadth-First Search (BFS)</strong>. Let’s traverse through this graph using both methods:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748378930021/c8c46f1e-493d-4790-a48b-5059359bd3a4.png" alt="An undirected graph with labeled nodes (A–E)" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-breadth-first-search-bfs">Breadth-First Search (BFS)</h3>
<p>BFS starts from a node and explores all of its immediate neighbors before moving on to the neighbors of those neighbors.</p>
<p>It’s commonly used in:</p>
<ul>
<li><p>Finding the shortest path (in unweighted graphs)</p>
</li>
<li><p>Level-order traversal</p>
</li>
<li><p>Network broadcast simulations</p>
</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bfs</span>(<span class="hljs-params">graph, start</span>):</span>
    visited = set()
    queue = deque([start])

    <span class="hljs-keyword">while</span> queue:
        node = queue.popleft()
        <span class="hljs-comment"># node has not been visited</span>
        <span class="hljs-keyword">if</span> node <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> visited:
            print(node)  <span class="hljs-comment"># Visit the node</span>
            visited.add(node)
            <span class="hljs-keyword">for</span> neighbor <span class="hljs-keyword">in</span> graph[node]:  <span class="hljs-comment"># Look at each neighbor of the current node</span>
                <span class="hljs-keyword">if</span> neighbor <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> visited:     
                    queue.append(neighbor)  

<span class="hljs-comment"># Undirected graph</span>
graph = {
    <span class="hljs-string">'A'</span>: [<span class="hljs-string">'B'</span>, <span class="hljs-string">'C'</span>],
    <span class="hljs-string">'B'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'D'</span>],
    <span class="hljs-string">'C'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'D'</span>],
    <span class="hljs-string">'D'</span>: [<span class="hljs-string">'B'</span>, <span class="hljs-string">'E'</span>],
    <span class="hljs-string">'E'</span>: [<span class="hljs-string">'D'</span>]
}

bfs(graph, <span class="hljs-string">'A'</span>)
</code></pre>
<p>Here’s the output of the code above:</p>
<pre><code class="lang-bash">A
B
C
D
E
</code></pre>
<h4 id="heading-what-this-code-does">What This Code Does:</h4>
<p><strong>Step 1: Choose a Starting Point</strong><br>Pick the node where you want to begin the traversal. Add it to a queue.</p>
<p><strong>Step 2: Visit the Node at the Front of the Queue</strong><br>Take the first node from the queue (this is your current node) and mark it as visited. Do something with it, like printing its value.</p>
<p><strong>Step 3: Add Its Neighbors to the Queue</strong><br>Look at all the nodes directly connected to the current node (its neighbors). For each neighbor, if it hasn't been visited yet, add it to the end of the queue.</p>
<p><strong>Step 4: Repeat Until the Queue Is Empty</strong><br>Go back to Step 2. Keep visiting the node at the front of the queue and enqueue its unvisited neighbors until no more nodes are left in the queue.</p>
<h3 id="heading-depth-first-search-dfs">Depth-First Search (DFS)</h3>
<p>DFS dives deep into one path of the graph before backtracking and exploring other branches.</p>
<p>It’s useful for:</p>
<ul>
<li><p>Cycle detection</p>
</li>
<li><p>Topological sorting</p>
</li>
<li><p>Solving puzzles like mazes (where the shortest path doesn’t matter)</p>
</li>
</ul>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">dfs</span>(<span class="hljs-params">graph, start</span>):</span>
    visited = set()
    stack = [start]

    <span class="hljs-keyword">while</span> stack:
        node = stack.pop()
        <span class="hljs-keyword">if</span> node <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> visited:
            print(node)  <span class="hljs-comment"># Visit the node</span>
            visited.add(node)
            <span class="hljs-keyword">for</span> neighbor <span class="hljs-keyword">in</span> graph[node]:
                <span class="hljs-keyword">if</span> neighbor <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> visited:
                    stack.append(neighbor)


<span class="hljs-comment"># Example graph (same as before)</span>
graph = {
    <span class="hljs-string">'A'</span>: [<span class="hljs-string">'B'</span>, <span class="hljs-string">'C'</span>],
    <span class="hljs-string">'B'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'D'</span>],
    <span class="hljs-string">'C'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'D'</span>],
    <span class="hljs-string">'D'</span>: [<span class="hljs-string">'B'</span>, <span class="hljs-string">'E'</span>],
    <span class="hljs-string">'E'</span>: [<span class="hljs-string">'D'</span>]
}

dfs(graph, <span class="hljs-string">'A'</span>)
</code></pre>
<p>Here’s the output of the code above:</p>
<pre><code class="lang-bash">A
C
D
E
B
</code></pre>
<h4 id="heading-just-like-bfs-we">Just like BFS, we:</h4>
<ul>
<li><p>Track visited nodes with a set</p>
</li>
<li><p>Use a loop to process each node</p>
</li>
<li><p>Visit neighbors one by one</p>
</li>
</ul>
<p><strong>But here’s the important difference</strong>: DFS uses a <strong>stack</strong>, so it goes <em>deep</em> into one path until it can’t go further, then it backtracks and explores the next path. This makes it great for tasks like solving mazes or exploring all possibilities in a decision tree.</p>
<h3 id="heading-wrapping-up-graph-traversal"><strong>Wrapping Up Graph Traversal</strong></h3>
<p>Understanding how to traverse a graph is foundational to solving many real-world problems, from mapping routes to analyzing social networks. Whether you're using BFS to find the shortest path or DFS to explore deep relationships, choosing the right traversal method depends on the problem you're trying to solve.</p>
<h2 id="heading-knights-travails-a-real-world-graph-problem">Knight's Travails: A Real-World Graph Problem</h2>
<p>To bring graphs to life, let’s start with a simple example.</p>
<p>Imagine you're playing chess, and your knight is stuck in one corner of the board. Let’s say you want to move it to a specific square to defend your queen. What’s the fewest number of moves it’ll take to get there?</p>
<p>This is a classic graph problem.</p>
<p>Each square on the chessboard can be represented as a node. If a knight can legally move from one square to another, there's an edge between them. The knight’s movement rules – those L-shaped hops – define the graph’s edges. So if you think about it, you're navigating a graph, trying to find the shortest path from one node (start square) to another (target square).</p>
<p>Let’s break it down.</p>
<h3 id="heading-modeling-the-board-as-a-graph">Modeling the Board as a Graph</h3>
<ul>
<li><p>The chessboard is an 8x8 grid.</p>
</li>
<li><p>Each square is a node, identified by its coordinates <code>(x, y)</code> where <code>0 ≤ x &lt; 8</code> and <code>0 ≤ y &lt; 8</code>.</p>
</li>
<li><p>A knight has a maximum of <strong>8 possible moves</strong> from any position (some may be out of bounds).</p>
</li>
<li><p>The goal is to build a graph where each node connects to all valid knight moves from that square.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748383043887/83609e20-d41d-47d1-ba19-25887a96abcc.jpeg" alt="A chessboard labeled with coordinate pairs along the bottom and left edges. A knight is positioned at (4, 0). The x-axis runs from (0, 0) to (0, 7) across the bottom, and the y-axis runs from (0, 0) to (7, 0) up the left side. The board visually shows how squares are represented as coordinate pairs in a grid." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-bfs-or-dfs-choosing-the-right-tool">BFS or DFS? Choosing the Right Tool</h3>
<p>There are two traversal methods we discussed earlier, Breadth-First Search (BFS) and Depth-First Search (DFS). So, which one should we use to solve the Knight’s Travails problem?</p>
<p>Let’s think about what would happen if we used DFS.</p>
<p>DFS goes as deep as it can along one path before backtracking. So, if you use DFS to move the knight, it might follow a long, winding sequence of valid moves that eventually leads to the destination, say, in 7 moves.</p>
<p>But that doesn’t mean it’s the shortest path. The optimal path could have reached the target in just 3 or 4 moves, but DFS wouldn’t find that unless it happens to explore that exact path first.</p>
<p>Even worse, DFS doesn’t guarantee you’ll find the shortest path unless you exhaustively search every possibility and compare them, which is slow and inefficient for this kind of problem.</p>
<p>Now, here’s why BFS is a better fit.</p>
<p>BFS explores all positions the knight can reach in 1 move, then all positions reachable in 2 moves, then 3 moves, and so on. As soon as it finds the destination square, you can be sure it got there using the fewest possible steps, because it explores all shorter paths before any longer ones.</p>
<p>In short:</p>
<ul>
<li><p>DFS might reach the destination eventually, but not efficiently, and not necessarily via the shortest path.</p>
</li>
<li><p>BFS guarantees that the path it returns is the shortest in terms of the number of moves.</p>
</li>
</ul>
<p>That’s why, for problems like this, where minimum steps are the goal, BFS is the go-to approach.</p>
<h3 id="heading-implementing-knights-travails-with-bfs">Implementing Knight’s Travails with BFS</h3>
<p>To model the knight’s traversal across the board, we’ll use a class called <code>Square</code>. This class will help us track:</p>
<ul>
<li><p>The knight’s current position on the board (<code>x_coord</code>, <code>y_coord</code>)</p>
</li>
<li><p>A reference to its parent square, so we can reconstruct the shortest path once the destination is reached</p>
</li>
<li><p>Keep track of all valid next moves (its <code>children</code>), which represent the squares the knight can legally move to from the current position</p>
</li>
</ul>
<p>Here’s the initial class:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque

<span class="hljs-comment"># Class to represent a square on the chessboard</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Square</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, x_coord, y_coord, parent=None</span>):</span>
        self.x_coord = x_coord
        self.y_coord = y_coord
        self.parent = parent
        self.children = []
</code></pre>
<p>This setup is useful because we’re building a path tree: each move creates a child node, and we can trace our way back to the starting point using the <code>parent</code>.</p>
<h4 id="heading-how-does-a-knight-move">How Does a Knight Move?</h4>
<p>A knight in chess moves in an “<strong>L</strong>” shape, two steps in one direction and one step perpendicular to it. If the knight is at the center of an 8x8 chessboard, it can make up to 8 legal moves. These moves can be visualized as edges connecting one square (node) to others.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748425100670/fbccffb8-4cc0-46eb-b67d-b206bd528bd6.jpeg" alt="A chessboard with a knight placed on e5. Dashed lines and red dots indicate the eight possible L-shaped moves the knight can make from that position. Each red dot marks a valid destination square according to the knight's movement pattern." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Here’s how we can represent and generate those moves:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque

<span class="hljs-comment"># Class to represent a square on the chessboard</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Square</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, x_coord, y_coord, parent=None</span>):</span>
        self.x_coord = x_coord
        self.y_coord = y_coord
        self.parent = parent
        self.children = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_legal_moves</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-comment"># All 8 possible knight moves (in L-shape)</span>
        row_moves = [<span class="hljs-number">-2</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">-2</span>]
        col_moves = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">-2</span>, <span class="hljs-number">-2</span>, <span class="hljs-number">-1</span>]

        <span class="hljs-keyword">for</span> dx, dy <span class="hljs-keyword">in</span> zip(row_moves, col_moves):
            nx, ny = self.x_coord + dx, self.y_coord + dy

            <span class="hljs-comment"># Only add valid board positions (0–7 for an 8x8 board)</span>
            <span class="hljs-keyword">if</span> <span class="hljs-number">0</span> &lt;= nx &lt; <span class="hljs-number">8</span> <span class="hljs-keyword">and</span> <span class="hljs-number">0</span> &lt;= ny &lt; <span class="hljs-number">8</span>:
                self.children.append(Square(nx, ny, self))
</code></pre>
<h4 id="heading-finding-the-shortest-path">Finding the Shortest Path</h4>
<p>Now that we have the <code>Square</code> class and the knight’s possible moves, we should implement BFS to find the shortest path from a starting square to a destination.</p>
<pre><code class="lang-python"><span class="hljs-comment"># BFS function to find the shortest path from start to end square</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">knight_travails</span>(<span class="hljs-params">start_coords, end_coords</span>):</span>
    <span class="hljs-comment"># Initialize the BFS queue with the starting square</span>
    start_x, start_y = start_coords
    queue = deque([Square(start_x, start_y)])
    <span class="hljs-comment"># Set to track visited positions and prevent revisiting</span>
    visited = set()
    end_coords = tuple(end_coords)

    <span class="hljs-keyword">while</span> queue:
        current = queue.popleft()
        coords = (current.x_coord, current.y_coord)

        <span class="hljs-comment"># If this square is the target, we've found the shortest path</span>
        <span class="hljs-keyword">if</span> coords == end_coords:
            <span class="hljs-keyword">return</span> current

        <span class="hljs-comment"># Skip, if this position has already been visited</span>
        <span class="hljs-keyword">if</span> coords <span class="hljs-keyword">in</span> visited:
            <span class="hljs-keyword">continue</span>

        visited.add(coords)

        <span class="hljs-comment"># Generate all legal knight moves from the current square</span>
        current.generate_legal_moves()
        <span class="hljs-comment"># Append children to queue</span>
        queue.extend(current.children)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">construct_path</span>(<span class="hljs-params">start, end</span>):</span>
    <span class="hljs-comment"># Run BFS to get the final square</span>
    end_square = knight_travails(start, end)
    path = []

    <span class="hljs-comment"># Trace back from the end square to the start using the parent links</span>
    <span class="hljs-keyword">while</span> end_square:
        path.append((end_square.x_coord, end_square.y_coord))
        end_square = end_square.parent

    <span class="hljs-comment"># Reverse the path to show it from start to end</span>
    <span class="hljs-keyword">return</span> path[::<span class="hljs-number">-1</span>]
</code></pre>
<p>Once you’ve implemented the functions above, you can print the output like this:</p>
<pre><code class="lang-python">print(construct_path([<span class="hljs-number">3</span>, <span class="hljs-number">4</span>], [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>]))
</code></pre>
<p>This returns the shortest sequence of moves the knight must take to travel from <code>[3, 4]</code> to <code>[0, 1]</code>. Sample output:</p>
<pre><code class="lang-python">[(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>), (<span class="hljs-number">2</span>, <span class="hljs-number">2</span>), (<span class="hljs-number">0</span>, <span class="hljs-number">1</span>)]
</code></pre>
<p>Each tuple represents a square on the chessboard that the knight visits. This is the shortest possible path the knight can take (from start to end square) – and thanks to how BFS works, we know for sure that no shorter path exists. That guarantee of optimality is what makes Breadth-First Search a perfect fit here.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>From chessboards to road maps and airline routes, graphs are everywhere. They silently power much of the technology we rely on daily. Whether you're booking a flight, navigating a city, or getting a recommendation on your favorite app, graphs are often at work behind the scenes.</p>
<p>They allow you to model and solve complex real-world problems by connecting entities and exploring their relationships. So next time your Google Maps reroutes you, remember: graphs are the mechanism behind that magic. Once you start recognizing graphs, you start seeing the hidden structure behind apps, systems, and even games you use every day.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How JavaScript Lint Rules Work (and Why Abstract Syntax Trees Matter) ]]>
                </title>
                <description>
                    <![CDATA[ Before I started to contribute to eslint-plugin-react, I didn’t think too deeply about the linters I used every day while writing code. Like many developers, I installed them at the start of a project, appreciated the red underlines or auto-fixes, an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-javascript-lint-rules-work-and-why-abstract-syntax-trees-matter/</link>
                <guid isPermaLink="false">682def7ddd9c9fdc3dae95cf</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ast ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tilda Udufo ]]>
                </dc:creator>
                <pubDate>Wed, 21 May 2025 15:21:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747835156597/f30994d4-f4da-4100-af25-9f858c015aa8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Before I started to contribute to <a target="_blank" href="https://github.com/jsx-eslint/eslint-plugin-react/">eslint-plugin-react</a>, I didn’t think too deeply about the linters I used every day while writing code. Like many developers, I installed them at the start of a project, appreciated the red underlines or auto-fixes, and moved on.</p>
<p>But behind those helpful messages is a powerful system of rules and structure that most of us rarely explore.</p>
<p>Linters are everywhere – across languages, frameworks, and workflows. They help catch errors, enforce consistent formatting, and promote best practices. They’re among the first tools we install in a new project, and yet they’re also some of the most underrated and least understood.</p>
<p>In this article, I’m going to take you under the hood. We’ll look at how JavaScript lint rules work, why ASTs (Abstract Syntax Trees) are such a big deal, and how you can use this understanding to write or contribute to a linter yourself.</p>
<h2 id="heading-table-of-contents">📚 Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-lint-rules-the-brains-behind-the-linter-what-even-is-a-linterwhat-even-is-a-linter">What Even Is a Linter?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-from-code-to-tree-enter-the-ast">From Code to Tree: Enter the AST</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-asts-matter-for-linting">Why ASTs Matter for Linting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-eslint-uses-asts-under-the-hood">How ESLint Uses ASTs Under the Hood</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-anatomy-of-a-lint-rule">Anatomy of a Lint Rule</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-helpful-tools-for-exploring-asts">Helpful Tools for Exploring ASTs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up-why-you-should-understand-this">Wrapping Up: Why You Should Understand This</a></p>
</li>
</ul>
<h2 id="heading-what-even-is-a-linter">🧹What Even Is a Linter?</h2>
<p>A linter is a tool that automatically analyzes your code to flag errors, enforce style rules<a class="post-section-overview" href="#how-eslint-uses-asts-under-the-hood">,</a> and catch potential bugs. Think of it as the Grammarly of the coding world – helping you write cleaner, more consistent code by pointing out problems early.</p>
<p>A popular example is <a target="_blank" href="https://eslint.org/">ESLint</a>, an open-source linter for JavaScript and TypeScript that checks code for issues and can even auto-fix some of them.</p>
<p>Linters are often:</p>
<ul>
<li><p>Integrated into your text editor or IDE</p>
</li>
<li><p>Run as part of a CI pipeline or pre-commit hook</p>
</li>
<li><p>Used alongside formatters like Prettier for even stricter consistency</p>
</li>
</ul>
<p>But how do they decide what to flag as an issue? That’s where <strong>lint rules</strong> come in.</p>
<h3 id="heading-lint-rules-the-brains-behind-the-linter">🧱 Lint Rules: The Brains Behind the Linter</h3>
<p>Lint rules are the building blocks of any linter. Each rule defines:</p>
<ol>
<li><p><strong>What to look for</strong>: a specific pattern in your code.</p>
</li>
<li><p><strong>What to do about it</strong>: a warning, an error, or an auto-fix.</p>
</li>
</ol>
<p>There are many types of rules, often grouped into categories like:</p>
<ul>
<li><p><strong>Error prevention</strong>: Catching bugs, like using undeclared variables.</p>
</li>
<li><p><strong>Code style</strong>: Enforcing consistent formatting and naming conventions.</p>
</li>
<li><p><strong>Best practices</strong>: Encouraging safer or more readable coding patterns.</p>
</li>
<li><p><strong>Security</strong>: Flagging risky code, like direct <code>eval()</code> calls or unsafe regex.</p>
</li>
</ul>
<p>If you’ve ever seen an ESLint message like this:</p>
<pre><code class="lang-bash">Unexpected console.log

Missing semicolon

<span class="hljs-string">'myVar'</span> is assigned a value but never used
</code></pre>
<p>…you’ve seen lint rules in action.</p>
<p>They’re not just “<strong>style police</strong>.” Linters help reduce mental overhead by catching little issues early, so you can focus on the bigger picture of what your code is trying to do.</p>
<h2 id="heading-from-code-to-tree-enter-the-ast">🌳 From Code to Tree: Enter the AST</h2>
<p>To understand how lint rules work under the hood, we need to talk about the <strong>Abstract Syntax Tree (AST)</strong> – the data structure at the heart of every linter.</p>
<p>An AST is a structured, tree-like representation of your code. Instead of reading your code as raw text, a linter converts it into a tree where each part of your code (a variable, a string, a function, and so on) becomes a <strong>node</strong> in the tree.</p>
<p>Here’s an example.</p>
<p>Paste this code into <a target="_blank" href="https://astexplorer.net/">AST Explorer</a>, a tool that lets you view the AST for code in real time:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> name = <span class="hljs-string">"Tilda"</span>;
</code></pre>
<p>Set the language to <strong>JavaScript</strong>, and choose one of the ESLint parsers like <strong>Espree</strong>. You’ll see something like this in the right panel:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747767760645/7929c578-7558-4a1b-8aa6-ed30399b090b.png" alt="AST (Abstract Syntax Tree) showing a VariableDeclaration node for a constant declaration. Inside it is a VariableDeclarator that assigns a Literal node with the string value 'Tilda' to an Identifier named 'name'." class="image--center mx-auto" width="1302" height="1150" loading="lazy"></p>
<p>In the image above from AST Explorer, you can see how the tree is structured:</p>
<ul>
<li><p><strong>Program:</strong></p>
<ul>
<li><p>The root node of the AST. It wraps the entire code.</p>
</li>
<li><p>Contains a <code>body</code>, which is an array of statements.</p>
</li>
</ul>
</li>
<li><p><strong>VariableDeclaration</strong></p>
<ul>
<li><p>Type: <code>"VariableDeclaration"</code></p>
</li>
<li><p>Represents a declaration using the <code>const</code> keyword.</p>
</li>
<li><p>Has a <code>kind</code> of <code>"const"</code> and a list of <code>declarations</code>.</p>
</li>
</ul>
</li>
<li><p><strong>VariableDeclarator</strong></p>
<ul>
<li><p>Type: <code>"VariableDeclarator"</code></p>
</li>
<li><p>Represents a single variable being declared.</p>
</li>
<li><p>Contains two key parts:</p>
<ul>
<li><p><strong>Identifier</strong></p>
<ul>
<li><p>Type: <code>"Identifier"</code></p>
</li>
<li><p>Name: <code>"name"</code></p>
</li>
<li><p>This is the variable being declared.</p>
</li>
</ul>
</li>
<li><p><strong>Literal</strong></p>
<ul>
<li><p>Type: <code>"Literal"</code></p>
</li>
<li><p>Value: <code>"Tilda"</code></p>
</li>
<li><p>This is the string being assigned to the variable.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>This nesting is what makes the structure <strong>“tree-like”</strong>. Each node is a parent to smaller pieces (its children), which helps linters navigate code reliably.</p>
<p>So while your eyes see a short line of JavaScript, the linter sees a detailed map of what that line <em>means</em> structurally. This hierarchy allows tools like ESLint to pinpoint exactly what kind of code is being used – and where – so rules can target patterns like:</p>
<ul>
<li><p>"Flag all <code>const</code> variables"</p>
</li>
<li><p>"Warn when a variable is named <code>name</code>"</p>
</li>
<li><p>"Disallow hardcoded strings like <code>Tilda</code>"</p>
</li>
</ul>
<h2 id="heading-why-asts-matter-for-linting">🤖 Why ASTs Matter for Linting</h2>
<p>Now, here’s the key idea: lint rules don’t work by reading your code like text. They work by matching specific node patterns in the AST.</p>
<p>That matters a lot because there are dozens of ways to write the same logic in JavaScript. Let’s take two versions of the same logic: one written as a <strong>function declaration</strong>, and one as an <strong>arrow function</strong>.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-string">"hello"</span>;
}

<span class="hljs-keyword">const</span> greet = <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"hello"</span>;
</code></pre>
<p>At a glance, they look different. But when we examine their ASTs, we see that both follow similar structural patterns. This is what allows a linter to recognize what your code is doing, no matter how it’s written.</p>
<h3 id="heading-the-tree-behind-the-function-declaration">🌳 The Tree Behind the Function Declaration</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747766773571/dfe619ca-d3a4-43a6-9018-c31e4abc6ed8.png" alt="Abstract Syntax Tree (AST) showing a FunctionDeclaration node with an Identifier for the function name. The function contains a BlockStatement with a ReturnStatement node. Inside the ReturnStatement is a Literal node returning the string 'hello'" class="image--center mx-auto" width="1302" height="1102" loading="lazy"></p>
<p>Here’s what ESLint sees in the AST tree when you write a function declaration:</p>
<ul>
<li><p>It starts with a <code>FunctionDeclaration</code> node.</p>
</li>
<li><p>That node contains:</p>
<ul>
<li><p>An <code>Identifier</code> (the function name: <code>greet</code>)</p>
</li>
<li><p>A <code>BlockStatement</code> representing the function body</p>
</li>
<li><p>Inside the <code>BlockStatement</code>, there’s a <code>ReturnStatement</code></p>
</li>
<li><p>The <code>ReturnStatement</code> returns a <code>Literal</code> — the string <code>"hello"</code></p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-the-tree-behind-the-arrow-function">🌳 The Tree Behind the Arrow Function</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747766822908/4723a1e9-c616-4b0d-bdde-ccf1f1cd6b0d.png" alt="Abstract Syntax Tree (AST) showing a VariableDeclaration node for a const arrow function. Inside it is a VariableDeclarator assigning an ArrowFunctionExpression to an identifier. The ArrowFunctionExpression contains a body with a Literal node returning the string 'hello'." class="image--center mx-auto" width="1302" height="1294" loading="lazy"></p>
<p>Here’s what ESLint sees when you write the same logic using an arrow function:</p>
<ul>
<li><p>A <code>VariableDeclaration</code> with <code>kind: "const"</code></p>
<ul>
<li><p>Inside it, a <code>VariableDeclarator</code>, which assigns a value to the <code>greet</code> variable</p>
</li>
<li><p>The value is an <code>ArrowFunctionExpression</code></p>
</li>
<li><p>The body of the arrow function is a <code>Literal</code> — the string <code>"hello"</code></p>
</li>
</ul>
</li>
</ul>
<p>Even though the syntax is different, both paths eventually lead to a <strong>Literal node</strong> containing <code>"hello"</code> – which is all your linter needs to care about.</p>
<h3 id="heading-lets-bring-it-home-with-an-example">💡 Let’s Bring It Home with an Example</h3>
<p>Imagine your team has a rule: functions shouldn’t return hardcoded strings like <code>"hello"</code>. You want a linter that flags this.</p>
<p>With ASTs, you can write <strong>one lint rule</strong> that matches a <code>ReturnStatement</code> or an <code>ArrowFunctionExpression</code> whose body is a <code>Literal</code>.</p>
<p>Here's the basic idea:</p>
<pre><code class="lang-javascript">ReturnStatement(node) {
  <span class="hljs-keyword">if</span> (node.argument?.type === <span class="hljs-string">"Literal"</span> &amp;&amp; node.argument.value === <span class="hljs-string">"hello"</span>) {
    context.report({ node, <span class="hljs-attr">message</span>: <span class="hljs-string">"Avoid returning static 'hello' strings."</span> });
  }
}
</code></pre>
<p>And for arrow functions with expression bodies:</p>
<pre><code class="lang-javascript">ArrowFunctionExpression(node) {
  <span class="hljs-keyword">if</span> (node.body?.type === <span class="hljs-string">"Literal"</span> &amp;&amp; node.body.value === <span class="hljs-string">"hello"</span>) {
    context.report({ node, <span class="hljs-attr">message</span>: <span class="hljs-string">"Avoid returning static 'hello' strings."</span> });
  }
}
</code></pre>
<p>Even though the code styles are different, the <strong>structure of the AST is similar enough</strong> that both functions will trigger the rule, because the linter isn’t looking at how the code is written, only what the structure of the AST actually is.</p>
<p>This is what makes ASTs so useful: they let linters ignore surface-level differences and focus on the actual meaning and structure of your code. As a result, you can write smarter, more flexible rules that catch patterns across different styles, no matter how someone wrote their JavaScript.</p>
<h2 id="heading-how-eslint-uses-asts-under-the-hood">🔨 How ESLint Uses ASTs Under the Hood</h2>
<p>ESLint relies on a standardized format called <a target="_blank" href="https://github.com/estree/estree">ESTree (ECMAScript Tree)</a> to represent JavaScript code as an Abstract Syntax Tree (AST). ESTree isn’t a parser itself – it’s a specification that defines how JavaScript code should be represented as a tree. This makes it possible for ESLint (and similar tools) to understand code in a consistent, structured way.</p>
<p>When you run ESLint on your code, here’s what’s happening under the hood:</p>
<h3 id="heading-1-your-code-is-parsed-into-an-ast"><strong>1. Your Code Is Parsed into an AST</strong></h3>
<p>ESLint converts your code into an AST that follows the ESTree format. This tree is made up of nodes, each representing a piece of your code (like a variable, function, or expression). The resulting structure is what every lint rule will analyze.</p>
<h3 id="heading-2-lint-rules-subscribe-to-specific-node-types"><strong>2. Lint Rules “Subscribe” to Specific Node Types</strong></h3>
<p>Each lint rule tells ESLint which <strong>node types</strong> it wants to listen to. For example, a rule might care about:</p>
<ul>
<li><p><code>Identifier</code></p>
</li>
<li><p><code>CallExpression</code></p>
</li>
<li><p><code>VariableDeclaration</code></p>
</li>
</ul>
<p>These node types match the structure you’d see in tools like AST Explorer.</p>
<h3 id="heading-3-eslint-traverses-the-tree-and-triggers-rules"><strong>3. ESLint Traverses the Tree and Triggers Rules</strong></h3>
<p>ESLint walks through the AST, visiting one node at a time. When it reaches a node type that a rule has subscribed to, it triggers the corresponding function in that rule.</p>
<p>This process is efficient and declarative, you don’t have to worry about manually scanning through every line of code. ESLint does the walking, your rule just listens.</p>
<h3 id="heading-4-rules-inspect-nodes-and-report-problems"><strong>4. Rules Inspect Nodes and Report Problems</strong></h3>
<p>Inside each rule, you receive the node ESLint has passed in. You can look at its properties – like name, value, or surrounding structure – and decide whether it violates your intended pattern.</p>
<p>If it does, you use <code>context.report()</code> to tell ESLint to flag it as an issue. ESLint can also fix the issue automatically if you provide a <code>fix()</code> function inside <code>context.report()</code>.</p>
<pre><code class="lang-javascript">context.report({
    <span class="hljs-attr">node</span>: node,
    <span class="hljs-attr">message</span>: <span class="hljs-string">"Missing semicolon"</span>.
    fix: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fixer</span>) </span>{
        <span class="hljs-keyword">return</span> fixer.insertTextAfter(node, <span class="hljs-string">";"</span>);
    }
});
</code></pre>
<h2 id="heading-anatomy-of-a-lint-rule">🩻 Anatomy of a Lint Rule</h2>
<p>Let’s look at a very simple custom ESLint rule. This one flags any variable named <code>any</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">meta</span>: {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"problem"</span>,
    <span class="hljs-attr">docs</span>: {
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Disallow variables named 'any'"</span>,
    },
  },

  create(context) {
    <span class="hljs-keyword">return</span> {
      Identifier(node) {
        <span class="hljs-keyword">if</span> (node.name === <span class="hljs-string">'any'</span>) {
          context.report({
            node,
            <span class="hljs-attr">message</span>: <span class="hljs-string">"Don't use 'any' as a variable name."</span>
          });
        }
      }
    };
  }
};
</code></pre>
<p>🔎 <strong>What’s happening here:</strong></p>
<ul>
<li><p>The meta section provides info about the rule (used in ESLint docs and tooling).</p>
</li>
<li><p>The <code>create()</code> function defines which node types the rule listens for.</p>
</li>
<li><p><code>Identifier(node)</code> is triggered every time an identifier is found in the code.</p>
</li>
<li><p>If the identifier’s name is <code>any</code>, the rule calls <code>context.report()</code> to raise a warning.</p>
</li>
</ul>
<h2 id="heading-helpful-tools-for-exploring-asts"><strong>🛠 Helpful Tools for Exploring ASTs</strong></h2>
<p>Understanding ASTs can feel abstract at first, but some tools make the learning curve much smoother. These are especially helpful when you’re trying to visualize how your code translates into tree structures, or when debugging a custom rule.</p>
<h3 id="heading-1-ast-explorerhttpsastexplorernet"><strong>1.</strong> <a target="_blank" href="https://astexplorer.net/"><strong>AST Explorer</strong></a></h3>
<p>This is the most beginner-friendly and powerful tool for working with ASTs. You can:</p>
<ul>
<li><p>Paste in any JavaScript code</p>
</li>
<li><p>Choose an ESLint-compatible parser (like Espree)</p>
</li>
<li><p>Instantly see the AST structure on the right-hand side</p>
</li>
<li><p>Hover over tree nodes and see how they map to specific parts of your code</p>
</li>
</ul>
<p>If you're writing a custom rule, AST Explorer will likely become your best friend. It helps you figure out exactly which node type you need to target and what properties are available on that node.</p>
<h3 id="heading-2-eslints-rule-examples-and-tests"><strong>2. ESLint’s Rule Examples and Tests</strong></h3>
<p>Sometimes the best way to learn is to read real code. ESLint's core rules (or rules from popular plugins like <a target="_blank" href="https://github.com/jsx-eslint/eslint-plugin-react/">eslint-plugin-react</a>) include:</p>
<ul>
<li><p>Rule definitions</p>
</li>
<li><p>Test files showing code that <strong>should</strong> and <strong>shouldn’t</strong> trigger the rule</p>
</li>
<li><p>Fix examples (if the rule is auto-fixable)</p>
</li>
</ul>
<p>Browsing these helps you understand how real-world rules are structured and how the test setup works.</p>
<p>Tip: Look in the <code>tests/lib/rules/</code> or <code>lib/rules/</code> folders of ESLint or plugin repositories.</p>
<h3 id="heading-3-eslints-documentation"><strong>3. ESLint’s Documentation</strong></h3>
<p>ESLint has comprehensive documentation about working with rules:</p>
<ul>
<li><p><a target="_blank" href="https://archive.eslint.org/docs/2.0.0/developer-guide/working-with-rules">ESLint: Working with Rules</a></p>
</li>
<li><p><a target="_blank" href="https://eslint.org/docs/latest/extend/custom-rules">ESLint: Custom Rules</a></p>
</li>
</ul>
<h2 id="heading-wrapping-up-why-you-should-understand-this"><strong>✅ Wrapping Up: Why You Should Understand This</strong></h2>
<p>Understanding how ASTs work gives you superpowers when it comes to customizing and contributing to linting tools. Whether you're trying to enforce a specific pattern in your team’s codebase or want to contribute to a plugin like <a target="_blank" href="https://github.com/jsx-eslint/eslint-plugin-react/">eslint-plugin-react</a>, this knowledge will help you:</p>
<ul>
<li><p>🔧 <strong>Contribute to existing rules</strong> by understanding what they’re checking and how</p>
</li>
<li><p>🐛 <strong>Debug confusing linter behavior</strong> when rules fire unexpectedly (or not at all)</p>
</li>
<li><p>🛠 <strong>Write your own custom rules</strong> to enforce specific coding standards, project conventions, or best practices</p>
</li>
</ul>
<p>You don’t need to be a compiler expert or fully grasp every node type in the spec. You just need to recognize patterns, explore trees, and get comfortable identifying the nodes your rule cares about.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
