<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zaki-goumri</title>
    <description>The latest articles on DEV Community by Zaki-goumri (@zakigoumri).</description>
    <link>https://dev.to/zakigoumri</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%2F2264792%2F917bd45f-99a5-49c2-86ab-4e65691a1dc8.jpeg</url>
      <title>DEV Community: Zaki-goumri</title>
      <link>https://dev.to/zakigoumri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zakigoumri"/>
    <language>en</language>
    <item>
      <title>Some indexing data structures</title>
      <dc:creator>Zaki-goumri</dc:creator>
      <pubDate>Sun, 14 Dec 2025 14:35:36 +0000</pubDate>
      <link>https://dev.to/zakigoumri/some-indexing-data-structures-2hkg</link>
      <guid>https://dev.to/zakigoumri/some-indexing-data-structures-2hkg</guid>
      <description>&lt;p&gt;When working with databases, we often think about queries, schemas, and APIs. But underneath all of that lies a more fundamental concern:&lt;br&gt;
&lt;strong&gt;How does a database find data efficiently?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Indexes are the data structures that answer this question. They allow databases to locate records quickly without scanning entire files on disk.&lt;/p&gt;

&lt;p&gt;Designing indexes for disk-based storage is very different from designing in-memory data structures. Disk access is slow, memory is limited, crashes must be handled safely, and data is constantly being updated. These constraints have led to specialized indexing structures that look very different from the data structures we use in application code.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore several core index data structures used in modern storage engines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hash indexes&lt;/strong&gt;, the simplest form of key-based indexing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sorted String Tables (SSTables)&lt;/strong&gt;, which store data in sorted order on disk&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log-Structured Merge Trees (LSM-Trees)&lt;/strong&gt;, which organize and merge SSTables efficiently at scale&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;B-Trees&lt;/strong&gt; , the classic indexing structure behind many databases&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than focusing on formal definitions, the goal is to build an &lt;strong&gt;intuitive understanding&lt;/strong&gt; of how these structures work, why they exist, and what trade offs they introduce. By the end, you should be able to reason about why different databases choose different index designs.&lt;/p&gt;

&lt;p&gt;Let’s begin with the simplest building block: &lt;strong&gt;the hash index&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hash Indexes
&lt;/h2&gt;

&lt;p&gt;When we start exploring database indexes, the simplest and most intuitive structure is the &lt;strong&gt;hash index&lt;/strong&gt;. Hash indexes are based on the same concept as in-memory hash maps or dictionaries in programming languages: a &lt;strong&gt;key maps directly to a value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a database, the goal is the same: quickly find the location of a record on disk using its key.&lt;/p&gt;


&lt;h3&gt;
  
  
  How a Hash Index Works
&lt;/h3&gt;

&lt;p&gt;Imagine a database that only &lt;strong&gt;appends data to a log file&lt;/strong&gt; on disk. Each record consists of a key-value pair. To find data efficiently, the database maintains an &lt;strong&gt;in-memory hash map&lt;/strong&gt; that stores the mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Key → Byte offset in the log file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever a new record is written:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Append it to the log file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update the in-memory hash map to point to the new offset.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When reading a value:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Look up the key in the hash map.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Seek to the offset in the log file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read the value.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjohgtszhmxp95ldaxmfq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjohgtszhmxp95ldaxmfq.jpg" title="Hash Index Concept – key to offset mapping" alt="Hash Index Concept – key to offset mapping" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Segment Files and Compaction
&lt;/h3&gt;

&lt;p&gt;If the database only appended to a single log file, it would &lt;strong&gt;grow indefinitely&lt;/strong&gt;. To solve this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Logs are split into &lt;strong&gt;segments&lt;/strong&gt; (files of fixed size).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Old segments are &lt;strong&gt;immutable&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;background compaction process&lt;/strong&gt; merges multiple segments, keeping only the latest value for each key.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Disk space is reclaimed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reads remain efficient.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - Old data is cleaned up without affecting ongoing writes.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Handling Deletes (Tombstones)
&lt;/h3&gt;

&lt;p&gt;To delete a key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A special record called a &lt;strong&gt;tombstone&lt;/strong&gt; is appended to the log.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;During compaction, the tombstone ensures that all previous values for that key are discarded.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps deletes consistent without needing in-place updates.&lt;/p&gt;




&lt;h3&gt;
  
  
  Crash Safety and Recovery
&lt;/h3&gt;

&lt;p&gt;Hash indexes are &lt;strong&gt;append-only&lt;/strong&gt;, which simplifies crash recovery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Partially written records are detected using &lt;strong&gt;checksums&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In-memory hash maps are rebuilt on restart either by scanning segments or loading a &lt;strong&gt;snapshot&lt;/strong&gt; saved on disk.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures the database can recover quickly even after a crash.&lt;/p&gt;




&lt;h3&gt;
  
  
  Concurrency
&lt;/h3&gt;

&lt;p&gt;Hash indexes are easy to make thread-safe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Usually, &lt;strong&gt;one writer thread&lt;/strong&gt; appends to the log.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multiple readers&lt;/strong&gt; can read segments concurrently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;h2&gt;
  
  
  Immutable segments eliminate the need for complex locking.
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Advantages of Hash Indexes
&lt;/h3&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fast exact key lookups (O(1) in memory).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simple design and high write throughput.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy crash recovery due to append-only files.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory-bound&lt;/strong&gt;: all keys must fit in RAM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No ordering&lt;/strong&gt;: cannot efficiently perform range queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Not ideal for very large datasets&lt;/strong&gt; if the key space is huge.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These limitations motivate &lt;strong&gt;Sorted String Tables (SSTables)&lt;/strong&gt;, which preserve &lt;strong&gt;key order on disk&lt;/strong&gt; and form the foundation for &lt;strong&gt;LSM-Trees&lt;/strong&gt;, which we’ll discuss next.&lt;/p&gt;




&lt;h1&gt;
  
  
  SSTables (Sorted String Tables)
&lt;/h1&gt;

&lt;p&gt;While hash indexes are fast for exact key lookups, they have some &lt;strong&gt;limitations&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;They must fit entirely in memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They do not preserve key order, so &lt;strong&gt;range queries&lt;/strong&gt; are inefficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On-disk hash maps are hard to maintain efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SSTables&lt;/strong&gt; solve these problems by storing &lt;strong&gt;key-value pairs in sorted order on disk&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is an SSTable?
&lt;/h3&gt;

&lt;p&gt;An SSTable (Sorted String Table) is a file that contains a sequence of key-value pairs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sorted by &lt;strong&gt;key&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each key appears &lt;strong&gt;only once&lt;/strong&gt; per SSTable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Immutable after being written to disk.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This design allows efficient merges, range queries, and reduces the need for a large in-memory index.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F36lzi78fdy40r1f7asf6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F36lzi78fdy40r1f7asf6.jpg" title="SSTable file layout" alt="SSTable file layout" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Constructing an SSTable
&lt;/h3&gt;

&lt;p&gt;Incoming writes are &lt;strong&gt;unordered&lt;/strong&gt;, so we maintain a &lt;strong&gt;memtable&lt;/strong&gt; in memory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Memtable = in-memory balanced tree (e.g., red-black tree) storing key-value pairs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the memtable exceeds a threshold (a few MB), write it to disk as an SSTable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;While writing the SSTable, a new memtable handles incoming writes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Reading from SSTables
&lt;/h3&gt;

&lt;p&gt;To find a key:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Check the &lt;strong&gt;memtable&lt;/strong&gt; first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check the &lt;strong&gt;most recent SSTable&lt;/strong&gt;, then the next-most-recent, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Because SSTables are &lt;strong&gt;sorted&lt;/strong&gt;, we can use &lt;strong&gt;sparse in-memory indexes&lt;/strong&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Only store offsets for some keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scan a few KBs in the file to find the exact key.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Merging and Compaction
&lt;/h3&gt;

&lt;p&gt;SSTables are &lt;strong&gt;immutable&lt;/strong&gt;, so updates and deletes generate new SSTables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Old SSTables are merged and compacted in the background.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;During merge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep only the &lt;strong&gt;most recent value&lt;/strong&gt; for each key.&lt;/li&gt;
&lt;li&gt;Discard obsolete or deleted entries.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Result = fewer files, sequential writes, and efficient storage.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Advantages of SSTables
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Preserve &lt;strong&gt;sorted order&lt;/strong&gt;, enabling &lt;strong&gt;range queries&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutable&lt;/strong&gt;, which simplifies concurrency and crash recovery.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can store datasets &lt;strong&gt;larger than memory&lt;/strong&gt; efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Background compaction keeps disk usage optimal.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reads may need to check &lt;strong&gt;multiple SSTables&lt;/strong&gt; to find a key.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Writes generate &lt;strong&gt;temporary files&lt;/strong&gt; and require compaction.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These challenges are addressed by &lt;strong&gt;LSM-Trees&lt;/strong&gt;, which organize multiple SSTables into a &lt;strong&gt;hierarchy&lt;/strong&gt; for high write throughput and efficient reads.&lt;/p&gt;




&lt;p&gt;Next, we can write the &lt;strong&gt;LSM-Tree section&lt;/strong&gt; in the same style, showing &lt;strong&gt;how it uses SSTables&lt;/strong&gt;, handles &lt;strong&gt;compaction&lt;/strong&gt;, and integrates &lt;strong&gt;Bloom filters&lt;/strong&gt; for fast non-existent key lookups.&lt;/p&gt;




&lt;h1&gt;
  
  
  LSM-Trees (Log-Structured Merge-Trees)
&lt;/h1&gt;

&lt;p&gt;While SSTables solve the problems of hash indexes by keeping keys sorted and enabling range queries, &lt;strong&gt;reading a key may still require checking multiple SSTables&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Log-Structured Merge-Trees (LSM-Trees)&lt;/strong&gt; organize SSTables into a &lt;strong&gt;hierarchical structure&lt;/strong&gt; to optimize both &lt;strong&gt;writes&lt;/strong&gt; and &lt;strong&gt;reads&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is an LSM-Tree?
&lt;/h3&gt;

&lt;p&gt;An LSM-Tree is essentially a &lt;strong&gt;cascade of SSTables&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memtable&lt;/strong&gt; (in-memory balanced tree) receives incoming writes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the memtable fills up, it is &lt;strong&gt;flushed to disk as an SSTable&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Older SSTables are gradually &lt;strong&gt;merged and compacted&lt;/strong&gt; into larger SSTables at lower levels.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates multiple &lt;strong&gt;levels&lt;/strong&gt; of SSTables, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Level 0 = most recent SSTables&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Higher levels = older, merged SSTables&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  How Writes Work
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Write comes in → added to the &lt;strong&gt;memtable&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write is &lt;strong&gt;appended to a log&lt;/strong&gt; on disk (for crash recovery).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When memtable exceeds threshold → flush to a new SSTable at &lt;strong&gt;Level 0&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Background compaction merges SSTables into &lt;strong&gt;higher levels&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Writes are mostly &lt;strong&gt;sequential&lt;/strong&gt;, maximizing disk throughput.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Old SSTables are never modified; only merged into new SSTables.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Reads Work
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Search &lt;strong&gt;memtable&lt;/strong&gt; first (fast, in-memory).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check &lt;strong&gt;Level 0 SSTables&lt;/strong&gt;, then &lt;strong&gt;Level 1&lt;/strong&gt;, and so on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To reduce disk reads, LSM-Trees use &lt;strong&gt;Bloom filters&lt;/strong&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Memory-efficient structure that &lt;strong&gt;checks if a key does not exist&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoids reading SSTables unnecessarily.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Compaction Strategies
&lt;/h2&gt;

&lt;p&gt;Compaction ensures that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Duplicate keys are merged, keeping the &lt;strong&gt;most recent value&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deleted keys (tombstones) are purged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disk usage stays manageable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Popular strategies:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Size-tiered compaction:&lt;/strong&gt; Merge smaller SSTables into larger ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leveled compaction:&lt;/strong&gt; SSTables are organized in levels; each level contains non-overlapping key ranges.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Advantages of LSM-Trees
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Extremely &lt;strong&gt;high write throughput&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can handle &lt;strong&gt;datasets larger than memory&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Efficient &lt;strong&gt;range queries&lt;/strong&gt; due to sorted SSTables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Crash recovery is simple (append-only log + immutable SSTables).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reads may need to check &lt;strong&gt;multiple levels&lt;/strong&gt; → slightly slower than in-memory hash indexes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compaction consumes &lt;strong&gt;CPU and I/O resources&lt;/strong&gt;, but it can be scheduled in the background.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  B-Trees
&lt;/h2&gt;

&lt;p&gt;While LSM-Trees and SSTables are optimized for &lt;strong&gt;write-heavy workloads&lt;/strong&gt;, &lt;strong&gt;B-Trees&lt;/strong&gt; are the most common indexing structure used in &lt;strong&gt;relational databases&lt;/strong&gt; and many key-value stores. They are optimized for &lt;strong&gt;balanced reads and writes&lt;/strong&gt; with efficient &lt;strong&gt;range queries&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is a B-Tree?
&lt;/h3&gt;

&lt;p&gt;A B-Tree is a &lt;strong&gt;self-balancing tree&lt;/strong&gt; where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Nodes (pages) store &lt;strong&gt;keys&lt;/strong&gt; and &lt;strong&gt;pointers to child nodes&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All &lt;strong&gt;leaf nodes&lt;/strong&gt; are at the same depth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nodes are designed to &lt;strong&gt;match the size of disk pages&lt;/strong&gt; (e.g., 4 KB) for efficient I/O.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Properties:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Keys in each node are &lt;strong&gt;sorted&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each node has a &lt;strong&gt;branching factor&lt;/strong&gt; (number of children).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tree height is kept &lt;strong&gt;low&lt;/strong&gt;, so reads involve &lt;strong&gt;few disk accesses&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftd2ngdcjsi4lnqc0fqss.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftd2ngdcjsi4lnqc0fqss.jpg" title="B_tree structure" alt="B tree " width="800" height="800"&gt;&lt;/a&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How Reads Work
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Start at the &lt;strong&gt;root node&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compare the key with the keys in the node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Follow the pointer to the &lt;strong&gt;child node&lt;/strong&gt; covering the key range.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repeat until reaching a &lt;strong&gt;leaf node&lt;/strong&gt;, which contains the key or a pointer to its value.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Lookup complexity = &lt;strong&gt;O(log n)&lt;/strong&gt;, typically just a few disk page reads.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Writes Work
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Locate the &lt;strong&gt;leaf node&lt;/strong&gt; for the key.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Insert the key and value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the node exceeds its capacity:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Split the node&lt;/strong&gt; into two.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update the &lt;strong&gt;parent node&lt;/strong&gt; with the new key and pointer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repeat recursively if necessary.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Pages are &lt;strong&gt;updated in place&lt;/strong&gt;, unlike LSM-Trees which append to files.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Crash Safety and Concurrency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Writes in B-Trees may require &lt;strong&gt;multiple pages&lt;/strong&gt; to be updated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To avoid corruption, databases often use a &lt;strong&gt;Write-Ahead Log (WAL)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Append changes to the log first.&lt;/li&gt;
&lt;li&gt;Apply changes to the tree.&lt;/li&gt;
&lt;li&gt;On crash, replay the WAL to restore consistency.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Concurrency is handled with &lt;strong&gt;latches&lt;/strong&gt; (lightweight locks) to prevent inconsistent reads during updates.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Advantages of B-Trees
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Efficient &lt;strong&gt;point lookups&lt;/strong&gt; and &lt;strong&gt;range queries&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Low tree height → few disk reads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Well-understood, widely implemented in relational databases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can handle &lt;strong&gt;in-place updates&lt;/strong&gt;, avoiding temporary file creation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Random writes can be &lt;strong&gt;slower&lt;/strong&gt; than sequential writes (e.g., LSM-Trees).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complex &lt;strong&gt;concurrency control&lt;/strong&gt; is needed for multiple writers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maintaining sorted order in-place can lead to &lt;strong&gt;fragmentation&lt;/strong&gt; over time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Comparison of Index Data Structures
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Structure&lt;/th&gt;
&lt;th&gt;Hash Index&lt;/th&gt;
&lt;th&gt;SSTable&lt;/th&gt;
&lt;th&gt;LSM-Tree&lt;/th&gt;
&lt;th&gt;B-Tree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key order&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unordered&lt;/td&gt;
&lt;td&gt;Sorted&lt;/td&gt;
&lt;td&gt;Sorted per SSTable&lt;/td&gt;
&lt;td&gt;Sorted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exact key lookup, small number of keys in memory&lt;/td&gt;
&lt;td&gt;Read-heavy workloads with mostly immutable data&lt;/td&gt;
&lt;td&gt;Write-heavy workloads, large datasets&lt;/td&gt;
&lt;td&gt;Balanced read/write, range queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read efficiency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very fast (single hash lookup)&lt;/td&gt;
&lt;td&gt;Moderate (sparse index + scan)&lt;/td&gt;
&lt;td&gt;Moderate to high (memtable + multiple SSTables, optimized with Bloom filters)&lt;/td&gt;
&lt;td&gt;High (O(log n), few page reads)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write efficiency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very high (append + update hash map)&lt;/td&gt;
&lt;td&gt;Moderate (requires creating new SSTable)&lt;/td&gt;
&lt;td&gt;Very high (sequential writes to memtable/SSTables)&lt;/td&gt;
&lt;td&gt;Moderate (in-place updates, node splits)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (hash map in memory)&lt;/td&gt;
&lt;td&gt;Low (sparse in-memory index)&lt;/td&gt;
&lt;td&gt;Low (memtable + Bloom filters)&lt;/td&gt;
&lt;td&gt;Moderate (depends on tree nodes in memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Crash recovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use snapshot of hash maps + append-only log&lt;/td&gt;
&lt;td&gt;Memtable + log for recent writes&lt;/td&gt;
&lt;td&gt;Memtable + log; SSTables immutable&lt;/td&gt;
&lt;td&gt;Write-ahead log (WAL) or copy-on-write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Range queries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easy (single writer)&lt;/td&gt;
&lt;td&gt;Easy (append-only SSTables)&lt;/td&gt;
&lt;td&gt;Easy (append-only + compaction in background)&lt;/td&gt;
&lt;td&gt;Complex (requires locks/latches)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Disk layout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Segments of key-value pairs&lt;/td&gt;
&lt;td&gt;Sorted immutable files&lt;/td&gt;
&lt;td&gt;Hierarchy of SSTables with levels&lt;/td&gt;
&lt;td&gt;Tree of fixed-size pages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Indexing is the backbone of &lt;strong&gt;efficient data retrieval&lt;/strong&gt; in databases and storage engines. Choosing the right index depends on &lt;strong&gt;your workload&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hash indexes&lt;/strong&gt; are perfect for fast, exact key lookups with a small set of keys in memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSTables&lt;/strong&gt; provide sorted storage with efficient range queries, laying the groundwork for LSM-Trees.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LSM-Trees&lt;/strong&gt; excel in &lt;strong&gt;write-heavy workloads&lt;/strong&gt; and can scale to datasets much larger than memory while maintaining efficient reads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;B-Trees&lt;/strong&gt; remain the go-to for &lt;strong&gt;relational databases&lt;/strong&gt;, offering balanced read/write performance, excellent range queries, and strong support for concurrency.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding the trade-offs of each structure helps developers and architects &lt;strong&gt;design systems tailored to their data access patterns&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>database</category>
      <category>performance</category>
    </item>
    <item>
      <title>GraphQL Deep Dive: How It Really Works Beyond the Basics</title>
      <dc:creator>Zaki-goumri</dc:creator>
      <pubDate>Sat, 23 Aug 2025 00:56:47 +0000</pubDate>
      <link>https://dev.to/zakigoumri/graphql-deep-dive-how-it-really-works-beyond-the-basics-5dia</link>
      <guid>https://dev.to/zakigoumri/graphql-deep-dive-how-it-really-works-beyond-the-basics-5dia</guid>
      <description>&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;GraphQL is often marketed as the “REST killer,” but that’s not the full story. It’s a query language for APIs that gives clients the power to request exactly the data they need. Behind the hype, GraphQL is still just requests and responses over a transport protocol like HTTP. To use it effectively, you need to understand not just what GraphQL is, but how it actually works under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;GraphQL is often marketed as the “REST killer,” but that’s not the full story. It’s a query language for APIs that gives clients the power to request exactly the data they need. Behind the hype, GraphQL is still just requests and responses over a transport protocol like HTTP. To use it effectively, you need to understand not just what GraphQL is, but how it actually works under the hood.&lt;/p&gt;

&lt;p&gt;In this article, we’ll break down GraphQL transports (HTTP, WebSockets, even TCP/UDP), why queries are sent via POST, how browsers handle requests, caching challenges, rate limiting, security, and the cost of parsing. We’ll also use GitHub’s GraphQL API as a concrete example.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. How GraphQL Moves Data
&lt;/h3&gt;

&lt;p&gt;By default, GraphQL runs over &lt;strong&gt;HTTP POST&lt;/strong&gt;. A client sends a JSON object containing three fields:&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="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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"variables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operationName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;Why POST?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GET requests can’t have a body.&lt;/strong&gt; That means the query has to be encoded into the URL as a query parameter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With nested queries, this quickly hits server/browser limits 2048 char in URL (e.g. a &lt;code&gt;414 URI Too Long&lt;/code&gt; error).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;POST avoids this problem by letting you send the query, variables, and operation name inside the request body as JSON so you got an unlimited HTTP request .&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Best practice: &lt;strong&gt;always use POST with &lt;code&gt;application/json&lt;/code&gt;&lt;/strong&gt;. Some libraries support GET for persisted queries (where the client sends just a hash and the server looks up the query), but for general use, POST is the reliable, future proof choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fetching the Schema
&lt;/h3&gt;

&lt;p&gt;One of GraphQL’s superpowers is that it’s self describing. A client can send an &lt;strong&gt;introspection query&lt;/strong&gt; to fetch the schema and understand what fields are available. Most servers expose this at &lt;code&gt;/graphql&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Example: Making a Request in the Browser
&lt;/h3&gt;

&lt;p&gt;Here’s how you’d query GitHub’s GraphQL API using &lt;code&gt;fetch&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.github.com/graphql&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&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;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;TOKEN&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="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;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
      query($login: String!) {
        user(login: $login) {
          name
          repositories(first: 3) {
            nodes { name }
          }
        }
      }
    `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;octocat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;then&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The response 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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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;The Octocat&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;repositories&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodes&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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;Hello-World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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;Spoon-Knife&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. GraphQL Over TCP, UDP, and WebSockets
&lt;/h3&gt;

&lt;p&gt;The GraphQL spec is &lt;strong&gt;transport-agnostic&lt;/strong&gt;. HTTP is just the most common choice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TCP&lt;/strong&gt;: You can send GraphQL queries over raw TCP sockets. Rare in practice, but possible for microservice communication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UDP&lt;/strong&gt;: Not realistic: GraphQL requires ordered, reliable delivery.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WebSockets&lt;/strong&gt;: Widely used for &lt;strong&gt;subscriptions&lt;/strong&gt; (real-time events). Example: a subscription to get new messages in a chat app.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;subscription&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="n"&gt;newMessage&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="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;sender&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="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;p&gt;Libraries like &lt;code&gt;graphql-ws&lt;/code&gt; or Apollo Subscriptions handle this in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Why Caching is Hard
&lt;/h3&gt;

&lt;p&gt;With REST, &lt;code&gt;GET /users/1&lt;/code&gt; maps to a specific resource and can be cached easily.&lt;br&gt;&lt;br&gt;
With GraphQL, everything goes through a single endpoint like &lt;code&gt;/graphql&lt;/code&gt;. To cache, you’d need to parse the query and understand which fields it requests.&lt;br&gt;
This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No &lt;code&gt;ETag&lt;/code&gt; or straightforward HTTP caching out of the box.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clients (Apollo, Relay) handle caching themselves.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A workaround is &lt;strong&gt;persisted queries&lt;/strong&gt; — send only a query hash to the server, which can then use normal caching.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  7. Headers, Rate Limiting &amp;amp; REST-like Concerns
&lt;/h3&gt;

&lt;p&gt;GraphQL still uses &lt;strong&gt;HTTP headers&lt;/strong&gt; just like REST.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Authorization&lt;/code&gt; for tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Custom headers as needed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rate limiting&lt;/strong&gt; is more complex than REST:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;REST: 1 request = 1 cost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GraphQL: 1 request could be “light” or “heavy” depending on how much data it fetches.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub solves this by assigning a &lt;strong&gt;point cost&lt;/strong&gt; to queries. You get a quota of points per hour. Querying 100 repositories costs more than querying 1.&lt;/p&gt;

&lt;p&gt;This means you need to &lt;strong&gt;design queries carefully&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Don’t request more fields than necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use pagination (&lt;code&gt;first&lt;/code&gt;, &lt;code&gt;after&lt;/code&gt;) to fetch large data sets gradually.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cache results client-side when possible.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  8. Complexity &amp;amp; Cost of Parsing
&lt;/h3&gt;

&lt;p&gt;One of the trade-offs with GraphQL is that every request has to be &lt;strong&gt;parsed, validated, and executed&lt;/strong&gt; — unlike REST, which usually just matches a URL to a handler.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Parse&lt;/strong&gt; – The raw query string is converted into an &lt;strong&gt;AST (Abstract Syntax Tree)&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AST is a structured tree representation of your query.&lt;/li&gt;
&lt;li&gt;Each field, argument, and nested selection becomes a node in that tree.&lt;/li&gt;
&lt;li&gt;Example:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"octocat"&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="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&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="n"&gt;nodes&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="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;AST (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Operation: Query
 └── Field: user (args: login="octocat")
      ├── Field: name
      └── Field: repositories (args: first=2)
           └── Field: nodes
                └── Field: name

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This structured tree makes it easier for the GraphQL engine to &lt;strong&gt;understand what’s being asked&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Validate&lt;/strong&gt; – The server checks the AST against the schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does &lt;code&gt;user&lt;/code&gt; exist as a type?&lt;/li&gt;
&lt;li&gt;Does &lt;code&gt;repositories&lt;/code&gt; belong to &lt;code&gt;user&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Are the arguments valid?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Execute&lt;/strong&gt; – The server walks the AST node by node, calling the corresponding resolvers to fetch the data.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why This Is Costly
&lt;/h4&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Parsing and validation take CPU time compared to REST’s simple “route → handler” model.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Deep or malicious queries can blow up execution time (&lt;code&gt;friends { friends { friends ... }}&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Servers must defend themselves against expensive queries.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Mitigations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Query depth limiting&lt;/strong&gt; – block queries that go too deep.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Query complexity analysis&lt;/strong&gt; – assign a “cost score” to queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persisted queries&lt;/strong&gt; – skip parsing/validation for known queries by storing their AST on the server.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  9. Security Concerns
&lt;/h3&gt;

&lt;p&gt;GraphQL opens the door to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DoS via expensive queries&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data exposure&lt;/strong&gt; if introspection is left on in production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Injection attacks&lt;/strong&gt; if resolvers are insecure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Disable introspection in prod (unless you need it).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add query depth/cost limits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sanitize inputs inside resolvers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10. Error Handling in GraphQL
&lt;/h3&gt;

&lt;p&gt;Error handling in GraphQL works differently than in REST. With REST, if something goes wrong you usually get an HTTP error code (400, 404, 500, etc.) and a message. In GraphQL, responses are always wrapped in JSON, and errors are returned in a dedicated errors field.&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
If you send a query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"does-not-exist"&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="n"&gt;name&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;The response might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;Could&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;does&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="n"&gt;exist&lt;/span&gt;&lt;span class="err"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="err"&gt;":&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;["&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="err"&gt;"]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="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;Notice a few things:&lt;/p&gt;

&lt;p&gt;The data field is still present (with null for the invalid part).&lt;/p&gt;

&lt;p&gt;The errors array contains messages, where they happened in the query (locations), and which path failed (path).&lt;/p&gt;

&lt;p&gt;The HTTP status is still 200 OK  because technically the request was valid, even though part of it failed.&lt;/p&gt;

&lt;p&gt;Types of Errors&lt;/p&gt;

&lt;p&gt;Validation errors – Query doesn’t match the schema (wrong field, wrong argument).&lt;/p&gt;

&lt;p&gt;Execution errors – Resolver fails (e.g., database issue, missing resource).&lt;/p&gt;

&lt;p&gt;Partial failures – Some fields resolve successfully, others fail. This is common and often useful — the client can still render partial data.&lt;/p&gt;

&lt;p&gt;Best Practices&lt;/p&gt;

&lt;p&gt;Don’t rely solely on HTTP codes. Expect errors in the response body.&lt;/p&gt;

&lt;p&gt;Add error extensions – You can attach custom fields to errors (e.g., error codes, internal tracking IDs). Example:&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;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Not authorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UNAUTHENTICATED"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;p&gt;Monitor for abuse Since HTTP 200 is returned even on logical errors, API monitoring should inspect both data and errors.&lt;/p&gt;

&lt;p&gt;Combine with headers For things like auth failures or rate limits, it’s common to still use HTTP headers (401 Unauthorized, 429 Too Many Requests) alongside the GraphQL error payload.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. When to Use and When Not To
&lt;/h3&gt;

&lt;p&gt;Use GraphQL when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You have complex UIs that need nested data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mobile clients need efficient, custom responses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You’re aggregating multiple backends into one schema.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid GraphQL when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You only need simple CRUD.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You rely heavily on CDN caching.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your team isn’t ready for the added complexity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  12. Wrap-Up
&lt;/h3&gt;

&lt;p&gt;GraphQL is powerful, but not a silver bullet. It trades &lt;strong&gt;simplicity and caching&lt;/strong&gt; for &lt;strong&gt;flexibility and efficiency&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Over HTTP POST by default, but can run over WebSockets or TCP.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Harder to cache, harder to rate limit, more costly to parse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Still uses headers, auth, and other REST-like patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Great for complex data fetching, risky for simple APIs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of GraphQL not as a replacement for REST, but as an option in your toolbox to be used when its strengths outweigh its complexity.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Designing Data-Intensive Applications: A Summary of Reliability, Scalability, and Maintainability</title>
      <dc:creator>Zaki-goumri</dc:creator>
      <pubDate>Fri, 21 Feb 2025 11:01:07 +0000</pubDate>
      <link>https://dev.to/zakigoumri/designing-data-intensive-applications-a-summary-of-reliability-scalability-and-maintainability-397e</link>
      <guid>https://dev.to/zakigoumri/designing-data-intensive-applications-a-summary-of-reliability-scalability-and-maintainability-397e</guid>
      <description>&lt;p&gt;Welcome to the first article in my &lt;strong&gt;series&lt;/strong&gt; summarizing one of the greatest books on software engineering: &lt;strong&gt;Designing Data-Intensive Applications&lt;/strong&gt; by Martin Kleppmann. This series aims to distill key concepts from the book, providing a quick refresher for those familiar with the material and a fast-paced introduction for those who prefer concise insights over reading entire books.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore three fundamental concerns in software engineering: &lt;strong&gt;reliability&lt;/strong&gt;, &lt;strong&gt;scalability&lt;/strong&gt;, and &lt;strong&gt;maintainability&lt;/strong&gt;. These are often referred to as &lt;strong&gt;non-functional requirements&lt;/strong&gt; or &lt;strong&gt;quality attributes&lt;/strong&gt;. They describe how a system should behave rather than what it should do, guiding its design, development, and operation.&lt;/p&gt;

&lt;p&gt;Modern data systems often combine multiple tools to handle massive workloads. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Message queues&lt;/strong&gt; like RabbitMQ, Kafka, and IBM MQ ensure reliable communication between services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In-memory databases&lt;/strong&gt; like Redis provide low-latency access to frequently accessed data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;APIs&lt;/strong&gt; abstract away implementation details, presenting a clean interface to clients.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When designing a data system or service, several questions arise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How do you ensure data remains correct and complete, even in the face of failures?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do you handle edge cases, such as network partitions or hardware failures?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do you provide good performance to clients while optimizing costs?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions highlight the importance of reliability, scalability, and maintainability in system design. Over the next few articles, we’ll explore each of these concepts in detail, starting with reliability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rtyg1rwhael5qcsoih5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rtyg1rwhael5qcsoih5.png" alt="Exemple of data-intesive application" width="299" height="168"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Reliability&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Reliability means the system continues to function correctly, even in the face of faults. A reliable system ensures that data is accurate, complete, and available when needed. Reliability depends on many factors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Design Quality&lt;/strong&gt;: Poor design or lack of proper planning can lead to frequent failures. For example, a system without fault tolerance may crash under unexpected conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware Quality&lt;/strong&gt;: Low-quality components or wear and tear can cause breakdowns. Redundancy (e.g., using multiple servers) can mitigate this risk.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Software Bugs&lt;/strong&gt;: Errors in the software code can lead to crashes or malfunctions. Rigorous testing and code reviews are essential to minimize bugs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: Lack of regular updates, fixes, or testing can reduce reliability. Proactive maintenance ensures the system stays robust over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workload&lt;/strong&gt;: Overloading a system beyond its capacity can cause failures. Proper capacity planning and load testing are critical.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;External Conditions&lt;/strong&gt;: Environmental factors like temperature, power surges, or network issues can affect performance. Designing for resilience (e.g., backup power supplies) helps mitigate these risks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redundancy&lt;/strong&gt;: A lack of backup systems or fail-safes can make a system less reliable. Redundancy ensures that failures in one component don’t bring down the entire system.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How to Improve Reliability&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Routine Maintenance&lt;/strong&gt;: Keep systems up-to-date and modernized through regular updates and patches.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redundancy&lt;/strong&gt;: Implement backup systems to prevent component failures from halting processes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quality Control&lt;/strong&gt;: Test system changes thoroughly before deploying them to production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitoring and Analysis&lt;/strong&gt;: Use comprehensive data collection and analysis to understand system reliability and performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Incident Communication&lt;/strong&gt;: Improve communication during incidents to reduce response and recovery time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Scalability&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Scalability is the capacity of a system to support growth or manage an increasing volume of work. A scalable system can handle more users, data, and traffic without sacrificing speed or reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Scalability Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Managing Growth&lt;/strong&gt;: Scalable systems can grow with your business, accommodating more users and data without performance degradation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improving Performance&lt;/strong&gt;: By distributing the load across multiple servers or resources, scalable systems achieve faster processing speeds and better response times.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ensuring Availability&lt;/strong&gt;: Scalability ensures systems remain operational even during traffic spikes or component failures.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost-Effectiveness&lt;/strong&gt;: Scalable systems adjust resources dynamically, avoiding over-provisioning and reducing costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Encouraging Innovation&lt;/strong&gt;: Scalability lowers infrastructure barriers, enabling the development of new features and services.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Measuring Scalability&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Scalability is not a one-dimensional metric—it depends on the specific &lt;strong&gt;load parameters&lt;/strong&gt; relevant to the system. These parameters vary depending on the system’s purpose. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Web Server&lt;/strong&gt;: Requests per second.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;: Ratio of reads to writes, number of simultaneous active users, or cache hit rate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chat System&lt;/strong&gt;: Number of messages sent per second or number of concurrent connections.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To describe scalability, you need to define the &lt;strong&gt;load parameters&lt;/strong&gt; and their &lt;strong&gt;distribution&lt;/strong&gt;. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A system might handle 10,000 requests per second, but if 90% of those requests are for the same piece of data (e.g., a popular video or post), the load distribution is skewed, and the system must be designed to handle such cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How to Scale&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before scaling, you need to understand your system’s &lt;strong&gt;load parameters&lt;/strong&gt; and &lt;strong&gt;performance goals&lt;/strong&gt;. Then, choose a scaling strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vertical Scaling (Scaling Up)&lt;/strong&gt;: Add more resources (e.g., CPU, memory, disk) to a single machine. This is simple but limited by the machine’s maximum capacity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Horizontal Scaling (Scaling Out)&lt;/strong&gt;: Add more machines to distribute the load. This is more complex but offers greater scalability and fault tolerance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Partitioning (Sharding)&lt;/strong&gt;: Split data across multiple machines to distribute the load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replication&lt;/strong&gt;: Store copies of data on multiple machines for fault tolerance and read scalability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Caching&lt;/strong&gt;: Store frequently accessed data in fast storage (e.g., Redis) to reduce latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Asynchronous Processing&lt;/strong&gt;: Use message queues (e.g., Kafka) to decouple tasks and handle them in the background.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Autoscaling&lt;/strong&gt;: Automatically add or remove resources based on load.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Maintainability&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Maintainability is the ability of a system to undergo repairs and modifications while remaining operational. A maintainable system is easy to understand, modify, and operate over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Principles of Maintainability&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Operability&lt;/strong&gt;: Make it easy for operations teams to keep the system running smoothly. This includes:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Providing good &lt;strong&gt;monitoring and alerting&lt;/strong&gt; tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Documenting &lt;strong&gt;runbooks&lt;/strong&gt; for common operational tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Designing for &lt;strong&gt;automation&lt;/strong&gt; (e.g., automated backups and scaling).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: Keep the system as simple as possible, avoiding unnecessary complexity. This involves:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using &lt;strong&gt;abstractions&lt;/strong&gt; to hide implementation details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Following the &lt;strong&gt;KISS principle&lt;/strong&gt; (Keep It Simple, Stupid).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refactoring regularly to remove technical debt.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Evolvability&lt;/strong&gt;: Make it easy to adapt the system to changing requirements. This requires:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Designing for &lt;strong&gt;modularity&lt;/strong&gt; (e.g., microservices, well-defined interfaces).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using &lt;strong&gt;backward-compatible&lt;/strong&gt; changes (e.g., versioned APIs).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing &lt;strong&gt;tests&lt;/strong&gt; to ensure changes don’t break existing functionality.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How to Improve Maintainability&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Good Documentation&lt;/strong&gt;: Document the system’s architecture, APIs, and operational procedures.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modular Design&lt;/strong&gt;: Break the system into small, independent components with clear interfaces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automated Testing&lt;/strong&gt;: Write unit, integration, and end-to-end tests to catch bugs early.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitoring and Observability&lt;/strong&gt;: Use metrics, logs, and distributed tracing to detect and diagnose issues quickly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Control and CI/CD&lt;/strong&gt;: Use version control (e.g., Git) and CI/CD pipelines to manage changes efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;In this article, we explored the three pillars of system design: &lt;strong&gt;reliability&lt;/strong&gt;, &lt;strong&gt;scalability&lt;/strong&gt;, and &lt;strong&gt;maintainability&lt;/strong&gt;. These non-functional requirements are critical for building systems that are robust, efficient, and adaptable over time. By understanding and applying these principles, you can design systems that meet the demands of modern applications.&lt;/p&gt;

&lt;p&gt;In the next article, we’ll dive deeper into &lt;strong&gt;data models and query languages&lt;/strong&gt;, exploring how different models (e.g., relational, document, graph) shape the design of data-intensive systems. Thank you for reading, and if you have any feedback or suggestions, please let me know in the comments!!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Blockchain</title>
      <dc:creator>Zaki-goumri</dc:creator>
      <pubDate>Wed, 05 Feb 2025 08:10:58 +0000</pubDate>
      <link>https://dev.to/zakigoumri/blockchain-1340</link>
      <guid>https://dev.to/zakigoumri/blockchain-1340</guid>
      <description></description>
      <category>blockchain</category>
    </item>
    <item>
      <title>Blockchain in express</title>
      <dc:creator>Zaki-goumri</dc:creator>
      <pubDate>Wed, 05 Feb 2025 08:10:29 +0000</pubDate>
      <link>https://dev.to/zakigoumri/blockchain-in-express-4g5m</link>
      <guid>https://dev.to/zakigoumri/blockchain-in-express-4g5m</guid>
      <description></description>
      <category>blockchain</category>
      <category>express</category>
    </item>
    <item>
      <title>Passport js</title>
      <dc:creator>Zaki-goumri</dc:creator>
      <pubDate>Thu, 12 Dec 2024 09:24:20 +0000</pubDate>
      <link>https://dev.to/zakigoumri/passport-js-4jmh</link>
      <guid>https://dev.to/zakigoumri/passport-js-4jmh</guid>
      <description></description>
      <category>javascript</category>
    </item>
  </channel>
</rss>
