<?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: Cesar Vega</title>
    <description>The latest articles on DEV Community by Cesar Vega (@gvegacl).</description>
    <link>https://dev.to/gvegacl</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1128584%2Fc27dccfd-e528-4c2d-8bd0-ecf3f4fcdda4.png</url>
      <title>DEV Community: Cesar Vega</title>
      <link>https://dev.to/gvegacl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gvegacl"/>
    <language>en</language>
    <item>
      <title>The URL Shortener System Design Lie: What Prep Guides Hand-Wave (And what production actually needs)</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Wed, 04 Nov 2026 13:17:56 +0000</pubDate>
      <link>https://dev.to/gvegacl/the-url-shortener-system-design-lie-what-prep-guides-hand-wave-and-what-production-actually-needs-1cka</link>
      <guid>https://dev.to/gvegacl/the-url-shortener-system-design-lie-what-prep-guides-hand-wave-and-what-production-actually-needs-1cka</guid>
      <description>&lt;p&gt;Recently I've been reading about system design, and I stumbled upon one of the "easiest" and "most basic" interview questions in system design: "Design A URL Shortener"&lt;/p&gt;

&lt;p&gt;I saw the problem in &lt;a href="https://bytebytego.com/courses/system-design-interview/design-a-url-shortener" rel="noopener noreferrer"&gt;Alex Xu's book&lt;/a&gt;, &lt;a href="https://systemdesign.one/url-shortening-system-design/" rel="noopener noreferrer"&gt;systemDesignOne&lt;/a&gt;, &lt;a href="https://blog.algomaster.io/p/design-a-url-shortener" rel="noopener noreferrer"&gt;algomaster&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=iUU4O1sWtJA" rel="noopener noreferrer"&gt;HelloInterview&lt;/a&gt;, etc... And I noticed that these resources tend to "handwave" a lot of depth, depth that a real interviewer will hone in on because we know many people use buzzwords without understanding the background.&lt;/p&gt;

&lt;h1&gt;
  
  
  The "handwaves"
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. NoSQL vs SQL
&lt;/h2&gt;

&lt;p&gt;Something I consistently saw across these guides is the disregard for explaining why SQL or NoSQL. In some cases, the authors mention it on one line, and that's it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A better option is to store  mapping in a relational database."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Other times, articles use the same substance-less justification:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Given these points, a NoSQL database like DynamoDB or Cassandra is a better option due to their ability to efficiently handle billions of simple key-value lookups and provide high scalability and availability."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I see these consistently:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"As the URL mappings are independent and don't have complex relationships, a NoSQL database is a good fit. MongoDB is a strong candidate because:&lt;br&gt;
It uses a leader-follower protocol, enabling the use of replicas for heavy read workloads.&lt;br&gt;
MongoDB ensures atomicity in concurrent write operations and returns duplicate-key errors to prevent collisions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I know the authors can't cover every topic, and many expect readers to know these concepts already. But this is a recipe for a candidate to stumble the moment someone questions these assumptions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interviewee&lt;/strong&gt;: "We'll use NoSQL because it can handle billions of simple key-value lookups"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewer&lt;/strong&gt;: "Can't SQL handle simple key-value lookups?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewee&lt;/strong&gt;: "Uhhhh, no?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most databases are already quite advanced, so either SQL or NoSQL will work for 99% of projects. For example, a Primary Key lookup in PostgreSQL (&lt;code&gt;SELECT long_url FROM urls WHERE short_code = '2TX';&lt;/code&gt;) traverses a B-Tree index in memory in $O(\log N)$ time (3 to 4 pointer hops), resolving in under 1 millisecond—matching DynamoDB single-digit millisecond latency over TCP. We will also focus on that 1% of projects at massive scale where database choice matters.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/22frg2" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So I intend to clarify the best database choice in depth for at least 3 scenarios: local scale, national scale, and global scale. And explain what actually matters when picking a database.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Single Point of Failure Crashes.
&lt;/h2&gt;

&lt;p&gt;This is one of my biggest gripes because so many articles say a variation of "We have more than one database so we are good" or "the provider will handle this for us"&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Implement automated failover mechanisms for the API and data store layers to switch to backup servers in case of failure."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Our core components (databases, caches, servers) support replication, which provides high availability and fault tolerance. The unique ID generation process also relies on a replicable database, which prevents a single point of failure."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And never mention this again. Or, even worse, don't mention single points of failure at all. This matters especially on smaller-scale systems: what if a single server or database handles all our traffic? - Which begets more questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;At what size should a new database or server be added?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU saturation, connection limits, and blast radius dictate a new server. In production, scale out compute nodes once sustained CPU exceeds ~70% or active socket connections saturate thread pools. At the same time, databases add read replicas when read latency spikes and scale storage volumes vertically long before horizontal partitioning is ever warranted.&lt;/li&gt;
&lt;li&gt;For example, on Amazon RDS, the cost of CPUs/RAM of the database server is separate from the storage (e.g., SSD); each GB costs about $0.23 USD (Multi-AZ), so for pure data growth you'll find yourself increasing storage capacity rather than server capacity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Can we get away with two tiny EC2 Amazon instances (&lt;code&gt;t4g.nano&lt;/code&gt;)?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yes, for low-throughput workloads (like a few hundred to thousands of requests daily), two &lt;code&gt;t4g.nano&lt;/code&gt; instances (0.5 GB RAM, 2 vCPUs) sitting across two Availability Zones behind an Application Load Balancer provide real high availability for negligible cost. The constraint on nano instances is not handling the traffic spikes—which burstable credits cover—but keeping the operating system and application memory footprint small enough to avoid Linux Out-Of-Memory (OOM) kills.&lt;/li&gt;
&lt;li&gt;One might confuse compute and storage here and think that having two instances is unnecessary if we are using a managed DB service (e.g., Amazon RDS) for two reasons:&lt;/li&gt;
&lt;li&gt;1. Managed database services already include a "multi-availability zone" option, so it's easy to assume we don't need two compute instances for resilience.&lt;/li&gt;
&lt;li&gt;2. The bulk of an instance's cost is CPU/RAM, not storage—so running multiple instances sounds like wasted money compared to a single instance with more storage capacity.&lt;/li&gt;
&lt;li&gt;But in reality, these are two separate layers: two &lt;code&gt;t4g.nano&lt;/code&gt; EC2 instances across AZs provide high availability for the &lt;strong&gt;stateless compute tier&lt;/strong&gt; (so our web app doesn't go down if one VM reboots), while an RDS Multi-AZ deployment provisions a second standby database instance in another AZ to provide high availability for the &lt;strong&gt;database tier&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Are Amazon instances already crash-resistant?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No, individual EC2 instances are isolated virtual machines that can and do crash, terminate during underlying hardware faults, or lose transient host capacity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do Amazon EC2 instances already have memory attached?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every EC2 instance has virtualized RAM allocated by the hypervisor. Still, that memory is strictly volatile—the instant an instance crashes or restarts, all in-memory data is erased, which is why stateful persistence is offloaded to remote network storage (EBS) or managed database engines.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My goal is for readers to understand the nuances behind saying, "Put it all on DynamoDB and let it be."&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/ldgdyf" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  3. How Is the Short URL Actually Made?
&lt;/h2&gt;

&lt;p&gt;So far, I've found three main approaches to shorten the original URL; all are valid, but I've noticed guides skip details of each implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1. The Hash and collision loop.
&lt;/h3&gt;

&lt;p&gt;These look deceptively simple, and most articles have some variation of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"To resolve hash collisions, we can recursively append a new predefined string until no more collisions are discovered."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"And truncated hashes collide: in 62^7 slots a collision becomes likely somewhere around a few million links, which you will pass on day one. You can check the table and retry, but now every create is a read plus a write, and the retry rate climbs as the table fills."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If a collision occurs, we append a predefined string or salt to the long URL and hash it again until we find a unique key."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But what they omit is details about how hashing works - Imagine this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interviewee&lt;/strong&gt;: "We can hash the long URL and get the first seven characters of the hash as our short URL"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewer&lt;/strong&gt;: "How would you prevent collisions between the first seven characters of the hash"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewee&lt;/strong&gt;: "I would append a predefined string or salt the long URL and hash it again"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewer&lt;/strong&gt;: "Would having a predefined string fix just one collision? What if you get more than two long URLs with the same seven characters? Would you also have to save in the database a list of salts?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewee&lt;/strong&gt;: "Yes?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The most important things to understand in this pattern are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Hashes are non-reversible and are not compression. A cryptographic hash (like MD5 or SHA-256) doesn't compress a 300-character URL; So hashes in this case are just a way of generating a "random" string with alphanumeric characters - it's no different from generating a random string.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Collisions happen earlier than most people realize; for a seven-character short URL (3.5 TRILLION possible URLs), collisions start happening just after a few million generations (~2.35 million due to the Birthday Paradox).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/ftjptx" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2. The Over-Engineered "Key Generation Service" (KGS)
&lt;/h3&gt;

&lt;p&gt;I don't think anyone in production should use something like this because it's complex and slow. It technically solves collisions, but it introduces a complex system that is hard to scale and manage.&lt;/p&gt;

&lt;p&gt;Defenders of this implementation could argue for its generality, including things like a snowflake/Twitter ID generator. Still, those unique ID generators can't be used for short URLs if we want 7-8 characters because a 64-bit Snowflake ID is 19 digits in decimal, and converting that 64-bit integer to Base62 still requires 11 characters (If this doesn't make a lot of sense, that's okay; it will be explained soon).&lt;/p&gt;

&lt;p&gt;A few examples of this implementation in online guides are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We can have a standalone Key Generation Service (KGS) that generates random six-letter strings beforehand and stores them in a database (let's call it key-DB). Whenever we want to shorten a URL, we'll take one of the pre-generated keys and use it. This approach makes things simple and fast. We also avoid encoding the URL, and we won't have to worry about duplicates or collisions. KGS will make sure all the keys inserted into key-DB are unique"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And then the same source handwaves the database overhead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Servers can use KGS to read/mark keys in the database. KGS can use two tables to store keys: one for unused keys, and one for all used keys. As soon as KGS gives keys to a server, it can move them to the used keys table. KGS can keep some keys in memory so it can provide them quickly whenever a server needs them. For simplicity, as soon as KGS loads some keys in memory, it can move them to the used keys table. This ensures each server gets unique keys. If KGS dies before assigning all the loaded keys, it can move them back to the used keys table."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This pattern also adds huge DB overhead by requiring synchronization between databases and continuously inserting, reading with locks, moving, and deleting rows.&lt;/p&gt;

&lt;p&gt;KGS also handwaves the complexity of server crashes (availability) and multiple databases; for example, do we have separate databases for used and unused keys? Are they in the same database? What if one database crashes? How do we decide which database to send the data to then?&lt;/p&gt;

&lt;p&gt;I've noticed candidates gravitate towards this solution when they want to design for large scale but lack specific knowledge of more streamlined patterns, like using Redis counters, which are single-threaded, or relying on MongoDB's internals; therefore, they can generate a count that will always be unique - we'll dive deeper into this later in the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3. The Counter + Base62: The Clean Math (And the Handwaved ID Generator)
&lt;/h3&gt;

&lt;p&gt;This is my preferred solution. But I've noticed sites often leave out details that can make or break someone's mental model. Here are a couple of examples:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Its INCR command atomically increments the Counter and returns the new value in a single operation. Because Redis is single-threaded, two simultaneous calls will always receive different values. If one gets 1000, the other gets 1001. This guarantee is what makes Redis ideal for distributed counter management. Each counter value is unique, eliminating the risk of collisions without the need for additional checks."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Challenges: In a distributed environment, maintaining a single global counter can be challenging due to synchronization issues. All instances of our Primary Server would need to agree on the counter value... Sequential counters also produce predictable short codes, making URL enumeration possible. An attacker could iterate through codes to discover all URLs."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the candidate memorizes this information without understanding the background, they will fall into a trap like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hash vs Radix: Sometimes candidates think "Base62" is like a hash function, but it is actually a Radix function. Here are the differences between them:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Hash Function (e.g., SHA-256, MD5)&lt;/th&gt;
&lt;th&gt;Base62 (Radix Representation)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A one-way mathematical function mapping variable-sized input to a fixed-size digest.&lt;/td&gt;
&lt;td&gt;A positional base-conversion system using 62 alphanumeric characters (&lt;code&gt;0-9&lt;/code&gt;, &lt;code&gt;a-z&lt;/code&gt;, &lt;code&gt;A-Z&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fingerprinting, integrity verification, or pseudo-random distribution.&lt;/td&gt;
&lt;td&gt;High-density positional notation: representing large Base-10 integers using fewer characters.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reversibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Irreversible&lt;/strong&gt; (one-way; original input cannot be computed back from the hash digest).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Fully Bijective&lt;/strong&gt; (two-way; can be decoded back to the exact starting integer via modulo/quotient arithmetic).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Collision Risk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Present&lt;/strong&gt; (different inputs can produce matching digests via the Birthday Paradox).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zero&lt;/strong&gt; (a unique input integer maps deterministically to a unique Base62 string).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output Length&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Fixed length&lt;/strong&gt; (e.g., SHA-256 is always 256 bits / 64 hex characters regardless of input length).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Dynamic length&lt;/strong&gt; (proportional to integer size: values $&amp;lt; 62^7$ fit within 7 characters).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Using a single-threaded counter without mentioning coordination when scaling, where the Counter lives, or what happens when your "counter server" crashes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In production, app servers don't call an external counter on every single write. They use &lt;em&gt;dynamic range leases&lt;/em&gt;. It's like fetching a block of 1,000 IDs at a time with &lt;code&gt;INCRBY 1000&lt;/code&gt; from an atomic coordinator (like Redis) and dispensing them locally in memory. If an application server crashes, the remaining IDs in that leased range are abandoned; because a 7-character Base62 namespace has over 3.5 trillion possible values, discarding a few hundred numbers during a crash costs practically nothing and avoids complex recovery locks. Meanwhile, the coordinator survives restarts by replaying its on-disk write-ahead log (such as Redis AOF) or synchronizing across Multi-AZ replicas with a forced offset jump to avoid reissuing duplicate IDs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/qs5sz6" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Security fixes: Many articles mention how using a sequential unique counter creates security issues; yet none show how to fix this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The fix is to pass the integer counter through a reversible, collision-free permutation—such as a lightweight Feistel cipher or an obfuscation tool like Sqids/Hashids—before converting to Base62. This scrambles sequential numbers into deterministic, random-looking alphanumeric tokens without risking duplicate keys.&lt;/li&gt;
&lt;li&gt;Remember: The generated short URL doesn't need a direct relationship to the long URL. Your short URL doesn't need to include any information about the long URL; it just needs to be unique.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/v3xsk2" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/965hkc" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Redis Read-through/Write-through/Cache aside
&lt;/h2&gt;

&lt;p&gt;When guides mention Redis, I've noticed many don't distinguish between Read-through/Write-through and cache-aside architectures. This distinction matters when discussing our system's trade-offs. Some examples I've seen:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"1. A user clicks a short URL link: &lt;a href="https://tinyurl.com/zn9edcu" rel="noopener noreferrer"&gt;https://tinyurl.com/zn9edcu&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The load balancer forwards the request to web servers.&lt;/li&gt;
&lt;li&gt;If a short URL is already in the cache, return the long URL directly.&lt;/li&gt;
&lt;li&gt;If a short URL is not in the cache, fetch the long URL from the database. If it is not in the database, the user likely entered an invalid short URL.&lt;/li&gt;
&lt;li&gt;The system returns the long URL to the user.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;The example doesn't even mention whether we're using cache-aside or read-through/write-through architectures. It might describe cache-aside, but it leaves the reader guessing about the details—and notice how Step 4 fetches from the database and Step 5 returns it to the user without ever writing the URL back into the cache!&lt;/p&gt;

&lt;p&gt;Or this other example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"After receiving a request, a web server first checks if the cache has the available response. If it has, it sends data back to the client. If not, it queries the database, stores the response in cache, and sends it back to the client. This caching strategy is called a read-through cache"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This example is misleading; what it describes is actually the definition of &lt;strong&gt;Cache-Aside&lt;/strong&gt;, not Read-Through. In a true &lt;strong&gt;Read-Through&lt;/strong&gt; cache, the application server talks &lt;em&gt;only&lt;/em&gt; to the cache layer. The cache engine itself owns the database driver, connects to PostgreSQL, and transparently pulls the record on a miss. In the real world, almost every production stack uses Cache-Aside because standard Redis has no database drivers or query logic to fetch data from PostgreSQL or DynamoDB on its own. Mislabeling Cache-Aside as Read-Through confuses candidates about who actually owns the database driver and SQL connection pool.&lt;/p&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We can cache URLs that are frequently accessed. We can use some off-the-shelf solution like Memcached, which can store full URLs with their respective hashes. The application servers, before hitting backend storage, can quickly check if the cache has the desired URL."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These examples barely explain how the hardware works or how it fits into our architecture. Are these services (Redis/Memcached) added on their own instance? How are they provisioned? What happens if they crash? Does Redis sit between the server and database as a proxy, or alongside the server as an internal backing service? If the same Redis cluster handles both the read cache (with LRU eviction) and the atomic ID counter (INCR), what stops Redis from evicting your counter key when memory fills up? If Redis crashes, does your Counter reset to zero? - All of these questions will be answered properly when we write our own implementations for three different scales.&lt;/p&gt;

&lt;p&gt;Guides also handwave what happens when you use Redis for counting and caching:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;As an Ephemeral Cache (Reads):&lt;/strong&gt; URLs are cached with an LRU (Least Recently Used) eviction policy to keep hot links in memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;As an Atomic Counter (Writes):&lt;/strong&gt; Redis executes &lt;code&gt;INCR&lt;/code&gt; or &lt;code&gt;INCRBY&lt;/code&gt; to hand out unique IDs for Base62 encoding.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Another crash could happen if you co-locate both roles on a single Redis instance configured with the standard &lt;code&gt;allkeys-lru&lt;/code&gt; (least recently used) maxmemory policy, because servers lease counter blocks in chunks (e.g., fetching 1,000 IDs at a time). That counter key sits idle between leases. When read traffic surges and Redis memory fills up, Redis's LRU algorithm will see the idle counter key and &lt;strong&gt;evict it from RAM&lt;/strong&gt;. To prevent Redis from evicting the Counter while running, change the configuration from &lt;code&gt;allkeys-lru&lt;/code&gt; to &lt;code&gt;volatile-lru&lt;/code&gt; (under &lt;code&gt;volatile-lru&lt;/code&gt;, Redis evicts only keys with an explicit expiration time, leaving our non-expiring counter key untouched) and/or physically separate the instances. To ensure the Counter doesn't reset to zero if the Redis server crashes and reboots, we need an Append-Only File (AOF) saved to disk or replicated across Availability Zones.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/mgtlqf" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Real sizes (Servers, databases).
&lt;/h2&gt;

&lt;p&gt;System design prep guides frequently create world-scale scenarios, inflate traffic numbers, and jump to massive abstract scales:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Because of the massive scale of billions of records, we must shard our database across multiple database servers using consistent hashing on the short URL."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or even examples like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Since we expect to have 500M new URLs every month and we would like to save this data for 5 years:&lt;br&gt;
Total number of URLs: 500M * 5 years * 12 months = 30 billion&lt;br&gt;
Total storage: 30 billion * 500 bytes = 15 TB"&lt;/p&gt;

&lt;p&gt;"To scale out our DB, we need to partition it so that it can store information about billions of URLs. We need to come up with a partitioning scheme that would divide and store our data into different DB servers."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These examples usually defend themselves by saying, "The cloud will handle it." Still, it's important to know how much servers actually cost, how much capacity they have, and how they differ.&lt;/p&gt;

&lt;p&gt;For instance, some guides calculate 15 TB for 30 billion URLs and claim we must partition across different DB servers. But in real-world infrastructure, a single standard AWS RDS PostgreSQL instance can attach up to 64 TB of storage on a single volume, and an Amazon Aurora cluster scales up to 128 TB automatically without sharding. 15 TB easily fits inside a single managed instance. At the same time, relying on a standalone, un-replicated database instance on a single server without synchronous replication or automated failover remains a catastrophic Single Point of Failure.&lt;/p&gt;

&lt;p&gt;Or what if you need a small, internal system? A single &lt;code&gt;db.t4g.micro&lt;/code&gt; instance (with 1 GB of RAM and a baseline 20 GB gp3 SSD) and a &lt;code&gt;t4g.nano&lt;/code&gt; server are more than enough, yet if that DB or server crashes, your whole system has multiple single points of failure. Imagine a scenario like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interviewee&lt;/strong&gt;: "So one single instance would be enough for this small-scale operation"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewer&lt;/strong&gt;: "So how would you handle redundancy? What would happen if that instance fails?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewee&lt;/strong&gt;: "The cloud will handle it?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many guides also gloss over provisioning. When we provision a managed cloud database like AWS RDS (&lt;code&gt;db.t4g.micro&lt;/code&gt;), we're renting an isolated virtual machine. If we run it as a Single-AZ (Availability Zone) instance, we still have a Single Point of Failure, so to make it resilient, we need to enable Multi-AZ (which provisions a synchronous hot standby in a different data center that automatically takes over if the primary dies). The problem is that guides often overlook the internals of this mechanism, and candidates often treat it as a magic phrase without realizing it solves failover resiliency, not write-throughput scaling.&lt;/p&gt;

&lt;p&gt;Candidates often have warped mental models about the physical reality of the servers. For example, Redis persistence doesn't work the same across bare metal and cloud providers. On a self-hosted Linux machine, Redis logs transactions directly to &lt;code&gt;/var/lib/redis/appendonly.aof&lt;/code&gt; on an attached SSD.&lt;/p&gt;

&lt;p&gt;However, on burstable managed cloud instances like AWS ElastiCache (&lt;code&gt;cache.t4g.micro&lt;/code&gt;), AWS turns off continuous AOF disk logging because constant disk syncing (&lt;code&gt;fsync&lt;/code&gt;) exhausts shared I/O credits and blocks the single-threaded event loop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note: Burstable instances and I/O credits&lt;/strong&gt;&lt;br&gt;
A burstable instance operates on an allowance: you get a modest baseline of CPU capacity (around 10%–20%) when idle, but you accumulate credits that let you burst up to 100% CPU when sudden traffic arrives.&lt;/p&gt;

&lt;p&gt;The issue is that storage operations follow a similar metering model. When you run an Append-Only File (AOF), every URL creation triggers an &lt;code&gt;INCR&lt;/code&gt; command, which forces an &lt;code&gt;fsync()&lt;/code&gt; to physically write those bytes from the operating system's buffer to disk. On a tiny, multi-tenant cloud instance, continuous &lt;code&gt;fsync()&lt;/code&gt; calls burn through your allocated I/O credits almost immediately. Once those credits drain to zero, disk access is throttled, causing writes that usually take under a millisecond to stall for 50–100ms. Because Redis executes all commands on a single thread, waiting on that throttled disk blocks the entire engine—freezing reads, writes, and all incoming network connections.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So instead of writing an on-disk transaction log, managed micro instances rely on network-level durability: the primary node asynchronously streams memory state to a hot replica in a different Availability Zone, while periodic binary snapshots (RDB files) are pushed to durable Amazon S3 storage for disaster recovery.&lt;/p&gt;

&lt;p&gt;Something else I've noticed is that guides tend to handwave about scaling a system progressively. If we are going to need 15 TB or 36 TB of data in 5 years, we shouldn't architect Day 1 for Year 5; we can size this for 12-24 months and then scale upwards if needed.&lt;/p&gt;

&lt;p&gt;I believe it's important to ground QPS/TPS/storage values in real offerings. That way, it's much easier to build a complete mental model.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Load Balancer vs. API Gateway vs. Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;I've noticed that many guides also use Load Balancers, API Gateways, and Reverse Proxies interchangeably. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The load balancer distributes incoming client requests across multiple servers to prevent any single server from becoming a bottleneck. The load balancer can be configured with routing algorithms such as round-robin, least connections, or IP hash."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While another guide ignores the load balancer completely and shows a diagram like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;[ Client ] ──► [ API Gateway ] ──► [ URL Service ] ──► [ Database ]&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Guides should be clear about this instead of glossing over it. In a real system, we might need both an API Gateway and a load balancer, or just one, depending on our architecture. Much of the confusion comes from these two being bundled together in modern managed offerings—such as cloud ingress controllers (like Azure Application Gateway Ingress Controller or GKE Ingress) that handle external Layer 7 (HTTP/HTTPS) traffic routing, TLS termination, and load balancing in a single layer.&lt;/p&gt;

&lt;p&gt;Even when bundled, an API Gateway and a Load Balancer are not interchangeable. The confusion stems from the fact that &lt;strong&gt;both API Gateways and Load Balancers are reverse proxies&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A Load Balancer&lt;/strong&gt; is a reverse proxy tuned for raw connection throughput and even traffic spreading. It is lightweight and consumes very few resources. Load balancers can also be confusing because they behave differently depending on which network layer they operate at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Layer 4 (Network Load Balancer):&lt;/strong&gt; Looks only at IP packets and TCP/UDP ports. It has no concept of HTTP, meaning it cannot see URL paths, headers, or request lines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 7 (Application Load Balancer):&lt;/strong&gt; Parses HTTP/HTTPS request lines, headers, cookies, and paths (e.g., routing &lt;code&gt;/api/*&lt;/code&gt; to Target Group A and &lt;code&gt;/*&lt;/code&gt; to Target Group B). However, it stops short of evaluating application-level business policy like JWT auth verification or user-specific rate limits. Because of this path routing, an ALB feels very close to an API Gateway.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An API Gateway&lt;/strong&gt; is a reverse proxy tuned for Layer 7 application policy (authentication, granular rate limiting, and route rewriting). It uses significantly more computational power to inspect payloads, validate cryptographic tokens, and enforce business rules.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another confusing point is that an API Gateway can technically serve as a Load Balancer. Many API Gateways (such as Kong, Envoy, or NGINX) let you configure upstream load-balancing algorithms like Round Robin or Least Connections across your backend instances.&lt;/p&gt;

&lt;p&gt;To make things more complex, Load Balancers can sit either "before" or "after" an API Gateway depending on how the system is set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The High-Scale / Kubernetes Setup (Load Balancer BEFORE Gateway):&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run gateway software like Envoy or Kong yourself, you need multiple gateway servers so the gateway itself doesn't crash or become a bottleneck. But how do internet users reach multiple gateway servers? You put a Layer 4 Network Load Balancer (NLB) in front to balance incoming TCP sockets across your gateway fleet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Standalone Gateway (No External Load Balancer):&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In smaller setups, or when using cloud-managed serverless gateways (like AWS API Gateway), you don't provision a separate load balancer in front of it. AWS API Gateway already has an internal ingress routing fleet built in. You point your custom domain name directly to API Gateway, and it routes requests to your backend microservices or serverless functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero API Gateways (Load Balancer Only):&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building a simple service that doesn't need API token tiers, client rate limits, or complex route rewrites, an Application Load Balancer (ALB) can terminate TLS (decrypting incoming HTTPS traffic) and use Round Robin to distribute HTTP requests straight to your compute nodes. The backend servers handle authentication and business logic themselves.&lt;/p&gt;

&lt;p&gt;So we can think of these three in an inheritance hierarchy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reverse Proxy:&lt;/strong&gt; Any server that sits between a client and a backend server, intercepting requests and responses so the backend stays hidden from the outside world.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Load Balancer (Reverse Proxy + Distribution):&lt;/strong&gt; A reverse proxy optimized to monitor backend health and spread traffic evenly. A Layer 4 LB inspects only raw TCP packets, while a Layer 7 LB inspects HTTP request lines and paths. Both offer ultra-low latency and consume minimal compute. It can send requests to an API Gateway fleet or directly to backend servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Gateway (Reverse Proxy + Load Balancer + Governance):&lt;/strong&gt; A specialized reverse proxy that does load distribution, but also inspects HTTP headers and payloads to enforce business rules—blocking unauthenticated callers, rejecting spam via token buckets, and rewriting URL paths before application code ever executes. Because it decrypts bearer tokens, parses request bodies, and validates schemas, it uses more compute and can introduce a slight latency penalty. It typically sends requests directly to backend servers, though in complex multi-tier setups it can route through internal load balancers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/8ywptq" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Consistent hashing
&lt;/h2&gt;

&lt;p&gt;Whenever system design articles discuss scaling past a single machine, they mention consistent hashing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Our design is horizontally scalable. The database can be sharded, and we can use consistent hashing to distribute load across both the application and database servers evenly."&lt;/p&gt;

&lt;p&gt;"The additional data store is partitioned using consistent hashing. The partition key is the long URL, so we can quickly find the URL record. A key-value store such as DynamoDB is used as the additional data store."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or fall back on basic modulo arithmetic:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We should also potentially consider sharding the database. We can shard it using some mapping from the URL (for example, the URL's hash code modulo some integer), which will allow us to locate the database which contains this file quickly."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Modulo hashing (&lt;code&gt;hash(key) % N&lt;/code&gt;) is an anti-pattern for dynamic systems (which most systems are): the moment you add or remove a single server, almost 100% of your keys map to entirely different machines, causing data thrashing or thundering herds. Consistent hashing solves this by ensuring that when a node joins or leaves, only a fraction of keys ($K/N$) get remapped.&lt;/p&gt;

&lt;p&gt;The problem is how guides illustrate it: a cosmic 360-degree circle floating in the middle of a whiteboard diagram, with keys and servers casually "mapped onto the ring."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Application Servers ] ──► ( The Floating Hash Ring ) ──► [ Database Shards ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This abstraction can lead to blind spots when an interviewer digs into the physical implementation:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Where Does the Ring Physically Live?
&lt;/h3&gt;

&lt;p&gt;Candidates often assume the hash ring is a standalone service or a central coordinator machine. But if 50 application servers have to make a network call to a "Hash Ring Coordinator" on every single request, that coordinator becomes a latency bottleneck and a single point of failure.&lt;/p&gt;

&lt;p&gt;In production, there is no ring server. The hash ring is an in-memory data structure (like a &lt;code&gt;TreeMap&amp;lt;Long, Node&amp;gt;&lt;/code&gt;) living directly inside the local process RAM of the server making the routing call to the database.&lt;/p&gt;

&lt;p&gt;When a server needs to route a short URL:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It hashes the key using a fast, non-cryptographic algorithm like MurmurHash3 on its own CPU.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It executes a local binary search in $O(\log N)$ time to find the nearest server pin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The lookup finishes in under a microsecond with zero network round-trips.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/fnpf8w" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  2. How Do Servers Coordinate Changes?
&lt;/h3&gt;

&lt;p&gt;If every application server keeps its own copy of the ring in local RAM, how do they stay synchronized when a database shard crashes or a new cache node boots up?&lt;/p&gt;

&lt;p&gt;Production architectures solve this through two distinct models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Centralized Coordination:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In systems using standalone cache pools (like Memcached) or sharded SQL instances, the storage nodes don't talk to each other. Instead, a small, independent cluster of coordinator machines (e.g., &lt;code&gt;t4g.small&lt;/code&gt; instances running &lt;code&gt;etcd&lt;/code&gt;, &lt;code&gt;Consul&lt;/code&gt;, or &lt;code&gt;ZooKeeper&lt;/code&gt;) continuously pings the storage nodes. Application servers keep an open watch listener with this coordinator fleet. When a storage machine drops offline, the coordinator alerts the application servers, which instantly recompute and swap their local in-memory tree pointers to route around the dead node.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Decentralized Gossip:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Distributed databases like Apache Cassandra or Amazon DynamoDB eliminate external coordinator servers. Each database server maintains the hash ring directly in process memory. The database machines continuously ping each other across an internal network port to exchange health updates ("Gossip"). If Database Machine C dies, Database Machines A and B detect the dropped heartbeat, spread the update cluster-wide, and independently adjust their local partition boundaries without needing an external manager.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/cp9yfh" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Can Load Balancers and API Gateways Use Consistent Hashing?
&lt;/h3&gt;

&lt;p&gt;Guides suggest using consistent hashing to "distribute load across both application and database servers." This creates confusion: should our Load Balancers or API Gateways use consistent hashing to distribute requests to stateless backend servers?&lt;/p&gt;

&lt;p&gt;For a stateless URL shortener - absolutely not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An Application Load Balancer's job is fair load distribution. Algorithms like Round Robin or Least Outstanding Requests spread traffic evenly across all web servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If your Load Balancer consistently hashes incoming requests based on the URL path (&lt;code&gt;GET /popular-deal&lt;/code&gt;), 100% of the traffic for that viral link will hammer the same backend server. That node will burn through CPU and crash while the remaining servers sit idle. Because stateless backend nodes read from a shared Redis cache or database cluster anyway, pinning specific URLs to specific app servers creates hot spots with no performance gain.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consistent hashing at the Gateway or Load Balancer tier makes sense only when the upstream servers hold in-memory, non-shared state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local In-Memory Cache Tiers:&lt;/strong&gt; If backend compute nodes maintain an LRU cache directly inside their own process memory instead of calling an external Redis instance, consistent hashing ensures repeat requests hit the host that already holds the cached data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stateful WebSockets:&lt;/strong&gt; Real-time chat applications (like Discord) use consistent hashing at the gateway to route users to the exact server hosting their active room connection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layer 4 Packet Balancing (e.g., Google Maglev):&lt;/strong&gt; Specialized edge load balancers use consistent hashing algorithms across router fleets so that network packet routes remain stable even if intermediate proxy boxes restart.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unless your compute nodes hold local state, consistent hashing belongs on the data-routing clients targeting partitioned caches or database shards, not on the front door distributing traffic across stateless application instances.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next steps
&lt;/h1&gt;

&lt;p&gt;Now that we have seen the many holes that guides tend to have, the next part of this article series will show how to design a URL shortener without leaving out any details. This way, any candidate asked this question will be prepared to go in depth at any point in their interview.&lt;/p&gt;

&lt;h1&gt;
  
  
  FAQ
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Implementation · Will the next article provide concrete code or architecture diagrams?
&lt;/h3&gt;

&lt;p&gt;Yes. Part 2 provides production VPC topologies and runnable code across three distinct tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Small Internal MVP (~500 writes/day):&lt;/strong&gt; Multi-AZ PostgreSQL using native &lt;code&gt;BIGINT GENERATED ALWAYS AS IDENTITY&lt;/code&gt; sequences, an Application Load Balancer, and a micro Redis instance for Cache-Aside reads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;National Consumer Scale (~5,000 writes/sec):&lt;/strong&gt; Horizontally autoscaling app clusters, dedicated Redis clusters separating atomic ID range leases from LRU read caches, and read replicas for uncached lookups.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Global Scale (100M+ writes/day):&lt;/strong&gt; Multi-region edge ingestion, decentralized token leases, and auto-partitioned storage (DynamoDB Global Tables).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Trade-offs · How do I actually defend SQL vs. NoSQL in an interview?
&lt;/h3&gt;

&lt;p&gt;Do not recite the generic textbook claim that "NoSQL is faster for key-value lookups". In memory, a PostgreSQL primary key lookup traverses a B-Tree index in $O(\log N)$ time (3 to 4 pointer hops) and resolves in under 1 millisecond—matching DynamoDB latency over TCP. Base your defense on two concrete physical factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write Idempotency (Deduplication):&lt;/strong&gt; If shortening an identical long URL twice must return the same short code, SQL guarantees this natively via an atomic &lt;code&gt;UNIQUE&lt;/code&gt; constraint on &lt;code&gt;long_url&lt;/code&gt;. In a NoSQL table partitioned by &lt;code&gt;short_url&lt;/code&gt;, querying by &lt;code&gt;long_url&lt;/code&gt; requires either a catastrophic full-table scan or an asynchronously replicated Global Secondary Index (GSI). Because GSIs are eventually consistent, concurrent writes for the same link create duplicate short codes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single-Writer Ceilings:&lt;/strong&gt; A single properly tuned relational database instance comfortably handles 10,000 to 20,000 indexed inserts per second and up to 64–128 TB of storage. Choose NoSQL (DynamoDB or Cassandra) only when sustained write throughput exceeds what a single primary can physically commit (30,000–50,000+ writes/sec) and you treat every link generation as a blind append without global deduplication.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Failure Handling · What happens during a cloud database failover?
&lt;/h3&gt;

&lt;p&gt;Availability is preserved by isolating failure domains rather than assuming managed cloud services eliminate downtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The 30–60s Failover Window:&lt;/strong&gt; If a Multi-AZ primary database crashes, automated DNS failover repoints traffic to the synchronous standby in 30 to 60 seconds. During this window, link &lt;em&gt;creation&lt;/em&gt; (&lt;code&gt;POST /shorten&lt;/code&gt;) fails, but link &lt;em&gt;redirects&lt;/em&gt; (&lt;code&gt;GET /{code}&lt;/code&gt;) continue resolving uninterrupted from the Redis Cache-Aside tier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Circuit Breakers:&lt;/strong&gt; Application servers must set aggressive socket timeouts and trip circuit breakers on database calls. If Redis misses a cold link during a database outage, the server must fail fast rather than hanging on dead database TCP connections and exhausting worker thread pools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Counter Recovery:&lt;/strong&gt; Application servers lease blocks of IDs (e.g., &lt;code&gt;INCRBY 1000&lt;/code&gt;) and treat remaining local tokens as ephemeral. If an app server crashes, discard its leased block. Discarding a few hundred IDs out of a 3.5-trillion Base62 namespace costs nothing and avoids complex distributed recovery locks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Security · How do I prevent sequential ID enumeration?
&lt;/h3&gt;

&lt;p&gt;Base62 is a positional base conversion, not encryption—incrementing an integer counter directly leaks predictable URLs (&lt;code&gt;/1&lt;/code&gt;, &lt;code&gt;/2&lt;/code&gt;, &lt;code&gt;/3&lt;/code&gt;) that scrapers can iterate over.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reversible Obfuscation:&lt;/strong&gt; Pass the 64-bit integer through a lightweight, collision-free Feistel cipher or permutation tool (like Sqids/Hashids) before encoding to Base62. This scrambles sequential increments into pseudo-random alphanumeric tokens without risking collisions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Edge Rate Limiting &amp;amp; 404 Heuristics:&lt;/strong&gt; Enforce sliding-window rate limits at your API Gateway or reverse proxy (e.g., 60 lookups/min per IP). Track 404-to-200 response ratios at the WAF; clients scanning non-existent short codes will hit abnormal miss ratios and can be automatically blocked at the perimeter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HMAC Signatures for Sensitive Links:&lt;/strong&gt; For private or single-use URLs (such as password resets or invoices), append a cryptographic signature token (&lt;code&gt;/xK9b?sig=...&lt;/code&gt;) validated at the gateway before redirecting.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Scaling · When should I actually shard a database?
&lt;/h3&gt;

&lt;p&gt;Never shard simply because an article mentions "billions of rows". 1 billion URLs at 500 bytes per row is only 500 GB, and 30 billion URLs is 15 TB—both fit comfortably on a single standard 64 TB AWS RDS volume or a 128 TB Aurora cluster without manual partitioning. Shard only when physical single-node thresholds break:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write IOPS &amp;amp; WAL Flush Limits:&lt;/strong&gt; When sustained write volume breaches ~20,000 to 40,000 writes/sec, where single-primary disk locks and write-ahead log (WAL) syncs become the hardware bottleneck.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-Region Latency &amp;amp; Edge Distribution:&lt;/strong&gt; When global users face 150ms+ cross-continental redirect latencies. Resolve this by deploying edge reverse proxies (Cloudflare/CloudFront) and multi-region read replicas (like Aurora Global Database) before attempting cross-region sharding.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  About the author:
&lt;/h3&gt;

&lt;p&gt;I'm a full-stack (frontend-leaning) Senior / Staff engineer. I'm currently exploring new engineering opportunities. If your team is tackling distributed systems challenges or needs someone who designs for production reality instead of interview buzzwords, connect with me on &lt;a href="https://www.linkedin.com/in/cesar-leonardo-vega-galvan/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or check out my work on &lt;a href="https://github.com/g-vega-cl" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>systemdesign</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>Skills, MCPs, and Commands are the same context engineering trend.</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Thu, 20 Nov 2025 21:54:01 +0000</pubDate>
      <link>https://dev.to/gvegacl/skills-mcps-and-commands-are-the-same-context-engineering-trend-49dp</link>
      <guid>https://dev.to/gvegacl/skills-mcps-and-commands-are-the-same-context-engineering-trend-49dp</guid>
      <description>&lt;h1&gt;
  
  
  What are skills:
&lt;/h1&gt;

&lt;p&gt;Skills are folders of instructions and scripts that Claude loads to do specialized tasks. &lt;strong&gt;The point of skills is to make Claude follow guidelines and be more deterministic.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More deterministic&lt;/li&gt;
&lt;li&gt;Easy to edit, share, and set up.&lt;/li&gt;
&lt;li&gt;Efficient context use.*&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Make AI more deterministic.
&lt;/h1&gt;

&lt;p&gt;This is one of the biggest issues with LLMs right now: they spout a lot of code, and they can and will come up with two different approaches and answers to the same prompt. With skills, we add guardrails, instructions, and scripts that tell the LLM exactly what to do. For example, the &lt;code&gt;playwright-skill&lt;/code&gt; I downloaded has a &lt;code&gt;helpers.js&lt;/code&gt; file which has a bunch of scripts like &lt;code&gt;launchBrowser&lt;/code&gt; or &lt;code&gt;safeClick&lt;/code&gt;, so Claude can use those functions and the result will always be the same.&lt;br&gt;
    * This was also a benefit that MCPs had.&lt;/p&gt;
&lt;h1&gt;
  
  
  Easy to edit.
&lt;/h1&gt;

&lt;p&gt;Since Skills are just &lt;code&gt;.md&lt;/code&gt; files and scripts, they are really easy to edit and share if necessary. The downside is that the files can be hard to maintain if they don't have a single source of truth.&lt;/p&gt;
&lt;h1&gt;
  
  
  Context
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Understanding Claude context
&lt;/h2&gt;

&lt;p&gt;Running &lt;code&gt;/context&lt;/code&gt; in Claude code (And in most other CLIs) gives you a breakdown of how context is used in the CLI:&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%2Fp97jaxtc7j1bvj5y8akm.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%2Fp97jaxtc7j1bvj5y8akm.png" alt=" " width="799" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The context is divided into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System prompt: The instructions set that defines Claude's identity, behavior, and core operational guidelines

&lt;ul&gt;
&lt;li&gt;Skills are loaded here.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;System tools: Built-in capabilities available in every conversation (file operations, execution, web access, agents).&lt;/li&gt;
&lt;li&gt;MCP tools: MCP tools and descriptions of each tool.&lt;/li&gt;
&lt;li&gt;Memory files: Project-specific context files (like CLAUDE.md) that provide persistent knowledge about your projects and preferences.&lt;/li&gt;
&lt;li&gt;Messages: The actual conversation history between you and Claude in the current session.&lt;/li&gt;
&lt;li&gt;Free space: Available context window capacity for additional content.&lt;/li&gt;
&lt;li&gt;Autocompact buffer: Reserved space that triggers automatic context compression when filled to prevent overflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Skills context
&lt;/h2&gt;

&lt;p&gt;Claude's path when using skills works like this:&lt;/p&gt;

&lt;p&gt;At the start of a session, Claude scans all available skill files and &lt;strong&gt;reads a short explanation for each one from the frontmatter YAML in the Markdown file&lt;/strong&gt; - this is very token efficient: each skill only takes up a few dozen extra tokens, with the full details only loaded in should the user request a task that the skill can help solve.&lt;/p&gt;

&lt;p&gt;For example, at the very beginning of each session, Claude generates a system context file. In which skills are loaded in a section like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &amp;lt;available_skills&amp;gt;
     &amp;lt;skill&amp;gt;
       &amp;lt;name&amp;gt;analyzing-financial-statements&amp;lt;/name&amp;gt;
       &amp;lt;description&amp;gt;This skill calculates key financial ratios and metrics from financial statement data &lt;span class="k"&gt;for &lt;/span&gt;investment analysis
  &lt;span class="o"&gt;(&lt;/span&gt;project&lt;span class="o"&gt;)&lt;/span&gt;&amp;lt;/description&amp;gt;
       &amp;lt;location&amp;gt;managed&amp;lt;/location&amp;gt;
     &amp;lt;/skill&amp;gt;
     &amp;lt;skill&amp;gt;
       &amp;lt;name&amp;gt;example-skill&amp;lt;/name&amp;gt;
       &amp;lt;description&amp;gt;Demonstrates the basic structure of a Claude Skill. Use this as a template when creating new skills. &lt;span class="o"&gt;(&lt;/span&gt;project&lt;span class="o"&gt;)&lt;/span&gt;&amp;lt;/description&amp;gt;
       &amp;lt;location&amp;gt;managed&amp;lt;/location&amp;gt;
     &amp;lt;/skill&amp;gt;
     &amp;lt;skill&amp;gt;
       &amp;lt;name&amp;gt;playwright-skill&amp;lt;/name&amp;gt;
       &amp;lt;description&amp;gt;Complete browser automation with Playwright. Auto-detects dev servers, writes clean &lt;span class="nb"&gt;test &lt;/span&gt;scripts to /tmp. Test pages, fill forms,
  take screenshots, check responsive design, validate UX, &lt;span class="nb"&gt;test &lt;/span&gt;login flows, check links, automate any browser task. Use when user wants to &lt;span class="nb"&gt;test
  &lt;/span&gt;websites, automate browser interactions, validate web functionality, or perform any browser-based testing. &lt;span class="o"&gt;(&lt;/span&gt;project&lt;span class="o"&gt;)&lt;/span&gt;&amp;lt;/description&amp;gt;
       &amp;lt;location&amp;gt;managed&amp;lt;/location&amp;gt;
     &amp;lt;/skill&amp;gt;
   &amp;lt;/available_skills&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You can see an example of the whole prompts at leaked prompts at &lt;a href="https://github.com/asgeirtj/system_prompts_leaks/blob/main/Anthropic/claude-4.5-sonnet.md" rel="noopener noreferrer"&gt;ageirtj's repo&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All my skills take approximately 200-250 tokens (including XML tags, descriptions, and metadata). The actual descriptions alone (just the text content) would be 35~40 tokens total.&lt;/p&gt;

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

&lt;p&gt;Claude only loads the .yaml-like description and data at the top of the document, and from there it decides if it's worth reading more (Progressive Disclosure):&lt;/p&gt;

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

&lt;h3&gt;
  
  
  NOTE: Skills depend on a coding environment
&lt;/h3&gt;

&lt;p&gt;One thing to note is that the skills mechanism is entirely dependent on the model having access to a filesystem, tools to navigate it, and the ability to execute commands in that environment. The filesystem is your computer, and it can change things in YOUR computer. So you should always verify whether your functions or commands have side effects that could affect your system.&lt;/p&gt;

&lt;p&gt;This can also be beneficial because you are not dealing with external dependencies, but it brings back the "It works in my computer" problem.&lt;/p&gt;

&lt;h1&gt;
  
  
  Skills vs MCP
&lt;/h1&gt;

&lt;p&gt;I mentioned that skills are not FULLY loaded upfront; they load the basic description, and then the agent decides whether it's worth reading more. MCPs used to be designed (And some still are) in a way that all of their tools were loaded up front, taking a lot of context. &lt;strong&gt;But new MCPs are improving their architecture, and they are using the same patterns as Skills to have separate code execution, which brings their context size to similar levels as Skills.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Note: Technically, you can use scripts in skills and avoid MCPs. (And vice-versa)&lt;/strong&gt;&lt;br&gt;
You can use both together: MCP connections give Claude access to tools, while Skills teach Claude how to use those tools effectively.&lt;br&gt;
MCP connects Claude to external services and data sources. Skills provide instructions for how to complete specific workflows. &lt;/p&gt;
&lt;h2&gt;
  
  
  Skills PROS:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Skills are simple&lt;/strong&gt;: The core simplicity of the skills design is why I'm so excited about it.
MCP is a whole protocol specification, covering hosts, clients, servers, resources, prompts, tools, sampling, roots, elicitation, and three different transports (stdio, streamable HTTP, and originally SSE). Skills are Markdown with a tiny bit of YAML metadata and some optional scripts in whatever you can make executable in the environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills are stable&lt;/strong&gt;: Since skills are just &lt;code&gt;.md&lt;/code&gt; files with scripts, they are usually very stable and simple to maintain.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  MCP PROS:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Can connect to external applications&lt;/strong&gt;: This is the biggest pro in my opinion; If you want an API-like interface that can connect to other applications, this is the way to go, especially since there are many officially supported MCPs from big organizations like Linear/Slack/Stripe. You can also benefit from MCP servers that run the code in the owner's cloud instead of locally.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Things to consider that I didn't delve into:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reliability, security/isolation, cross-device availability, latency, required runtime permissions, collaboration features, etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Plawright MCP vs skill example.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DISCLAIMER:&lt;/strong&gt; It's not the same maintainer, so some of the features in the MCP are not in the SKILL and vice versa.&lt;br&gt;
The Playwright skill uses around 100 tokens in context, while the MCP uses ONE HUNDRED times more (10'000 tokens). The main difference is that the Skill has a lot of the functionality the MCP has, but it gets loaded ONLY when it's necessary, while the MCP is always in memory.&lt;/p&gt;

&lt;p&gt;Here is a breakdown of the approximate token use that Playwrite MCP uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  Based on the JSON schema definitions visible to me, here&lt;span class="s1"&gt;'s a rough estimate:

  Simple tools (minimal parameters):
  - browser_close, browser_navigate_back, browser_install, browser_console_messages
  - ~150-250 tokens each

  Medium tools (moderate parameters):
  - browser_resize, browser_navigate, browser_press_key, browser_take_screenshot, browser_wait_for, browser_tabs
  - ~300-500 tokens each

  Complex tools (extensive parameters with nested schemas):
  - browser_evaluate, browser_fill_form, browser_click, browser_type, browser_drag, browser_hover, browser_select_option, browser_run_code
  - ~500-800 tokens each

  Rough total estimate: 8,000-10,000 tokens for all 22 Playwright MCP tools, including their full JSON schema definitions, descriptions, and parameters
  specifications, and metadata.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will say this again, &lt;strong&gt;10'000 tokens in context for something we might not use is not worth it&lt;/strong&gt;. I have personally disabled the Playwright MCP, but I had to change the Skill a bit to fit my work better (which is waay easier to do in Skills, since I have the files right there in my codebase).&lt;/p&gt;

&lt;h2&gt;
  
  
  Semi-outdated MCP criticisms (In my opinion):
&lt;/h2&gt;

&lt;p&gt;I saw this quote by Simon Willson, but I think the main point about spending tokens can be mitigated by good MCP design:&lt;br&gt;
    * "My own interest in MCPs has waned ever since I started taking coding agents seriously. Almost everything I might achieve with an MCP can be handled by a CLI tool instead. LLMs know how to call cli-tool --help, which means you don't have to spend many tokens describing how to use them—the model can figure it out later when it needs to."&lt;br&gt;
    * My comment about this is that there is a new trend with MCPs that do the same as Skills, they display a short description and then point to relevant files and scripts to be used. - But note: For most tasks, it's probably still better to use Skills.&lt;br&gt;
        - &lt;a href="https://www.anthropic.com/engineering/code-execution-with-mcp" rel="noopener noreferrer"&gt;https://www.anthropic.com/engineering/code-execution-with-mcp&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What about RAG?
&lt;/h1&gt;

&lt;p&gt;RAG is a concept that was very hyped before, and it's still relevant. RAG is a middle step where the agent searches and retrieves external data with whatever tools are available to it - grep, vector database, or any relevancy algorithm. &lt;/p&gt;

&lt;p&gt;It can be similar to Skills or MCPs in the way that it's a tool that an LLM can use to improve its answer.&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%2Fe7squqhnqltg4e88z6s0.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%2Fe7squqhnqltg4e88z6s0.jpg" alt=" " width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What about Claude commands?
&lt;/h1&gt;

&lt;p&gt;Custom slash commands allow you to define frequently-used prompts as Markdown files that Claude Code can execute. There are plenty of pre-loaded commands in Claude code like &lt;code&gt;/context&lt;/code&gt;, &lt;code&gt;/mcp&lt;/code&gt;, &lt;code&gt;/github&lt;/code&gt;, etc... &lt;br&gt;
They behave almost the same as skills in the way that they are some kind of script or prompt that the LLM runs when you tell it to. &lt;/p&gt;

&lt;p&gt;It seems that commands haven't gained a lot of traction, and I think it's because they tend to be focused on small changes. I think of Skills as an orchestrator of commands. If a command is a single function or bash, skills are a group of functions or commands.&lt;/p&gt;

&lt;h1&gt;
  
  
  Understanding that context is stateless,
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;We pass the whole conversation every time&lt;/em&gt;&lt;br&gt;
One thing that really helped me understand why keeping context clean is important is that LLMs are stateless; in every new message, we are just passing the whole conversation back and forth to the model (Maybe some providers optimize for this?). So every new message ends up being your whole conversation and the latest message attached to it. &lt;/p&gt;

&lt;p&gt;Sending a single-line message at the end of a 400-line discussion with a lot of back-and-forth is a lot different than sending one at the end of a 100-line structured discussion. MCPs and Skills can help us keep this context as small as possible and even save their thoughts in concise documents that we can reference in a new chat with an LLM.&lt;/p&gt;

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

&lt;p&gt;Claude, it's becoming a general agent, an orchestrator. Skills, Commands, MCPs, and Agents make this explicit. And we as developers will have to focus a lot more on managing Context and providing guardrails, scripts, and instructions to restrict the LLM when necessary and let it work in what it does best.&lt;/p&gt;

&lt;p&gt;Even when models are pushing context windows of 1 million tokens, they still struggle to keep track of things we have done in long conversations, so keeping things understandable and documented is still critical.&lt;/p&gt;

&lt;p&gt;Feel free to send me sources and comments about this.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sources:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://support.claude.com/en/articles/12512176-what-are-skills" rel="noopener noreferrer"&gt;https://support.claude.com/en/articles/12512176-what-are-skills&lt;/a&gt;&lt;br&gt;
&lt;a href="https://simonwillison.net/2025/Oct/16/claude-skills/" rel="noopener noreferrer"&gt;https://simonwillison.net/2025/Oct/16/claude-skills/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills" rel="noopener noreferrer"&gt;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/lackeyjb/playwright-skill?tab=readme-ov-file" rel="noopener noreferrer"&gt;https://github.com/lackeyjb/playwright-skill?tab=readme-ov-file&lt;/a&gt;&lt;br&gt;
&lt;a href="https://news.ycombinator.com/item?id=45619537" rel="noopener noreferrer"&gt;https://news.ycombinator.com/item?id=45619537&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/asgeirtj/system_prompts_leaks/blob/main/Anthropic/claude-4.5-sonnet.md" rel="noopener noreferrer"&gt;https://github.com/asgeirtj/system_prompts_leaks/blob/main/Anthropic/claude-4.5-sonnet.md&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.anthropic.com/engineering/code-execution-with-mcp" rel="noopener noreferrer"&gt;https://www.anthropic.com/engineering/code-execution-with-mcp&lt;/a&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/what-is/retrieval-augmented-generation/" rel="noopener noreferrer"&gt;https://aws.amazon.com/what-is/retrieval-augmented-generation/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://code.claude.com/docs/en/slash-commands" rel="noopener noreferrer"&gt;https://code.claude.com/docs/en/slash-commands&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" rel="noopener noreferrer"&gt;https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>llm</category>
    </item>
    <item>
      <title>In Cursor, Context is King.</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Sat, 05 Jul 2025 17:07:15 +0000</pubDate>
      <link>https://dev.to/gvegacl/in-cursor-context-is-king-3ai7</link>
      <guid>https://dev.to/gvegacl/in-cursor-context-is-king-3ai7</guid>
      <description>&lt;h1&gt;
  
  
  Executive summary
&lt;/h1&gt;

&lt;p&gt;If there is one thing you should take away from this document, it is to "Document what you are doing in a separate file."&lt;br&gt;
If you have time for a second thing, it is "Add context manually." &lt;/p&gt;
&lt;h1&gt;
  
  
  Understanding Context.
&lt;/h1&gt;
&lt;h2&gt;
  
  
  &lt;a href="https://docs.cursor.com/guides/working-with-context" rel="noopener noreferrer"&gt;Context window&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;First, what is a context window? And how does it relate to effectively coding with Cursor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A context window is all the context an LLM has when answering a prompt.&lt;/p&gt;

&lt;p&gt;In Cursor, "context" is the information that is provided to the model. There are two types of context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intent context&lt;/strong&gt; defines what the user wants to get out of the model. For example, a system prompt usually serves as high-level instructions for how the user wants the model to behave. Most of the "prompting" done in Cursor is intent context. "Turn that button from blue to green" is an example of stated intent; it is prescriptive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State context&lt;/strong&gt; describes the state of the current world. Providing Cursor with error messages, console logs, images, and chunks of code are examples of context related to state. It is descriptive, not prescriptive.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Providing context to Cursor.
&lt;/h2&gt;

&lt;p&gt;When answering a prompt, Cursor will try to find context by itself, it does this with a &lt;code&gt;proprietary&lt;/code&gt; model that pulls in the parts of your codebase that the model estimates are relevant, such as the current file, semantically-similar patterns in other files, and other information from your session. It also might use "tools" and MCPs like web search to find relevant context.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;proprietary&lt;/code&gt; model is pretty good, but it falls short when dealing with complex problems. &lt;/p&gt;

&lt;p&gt;I made an experiment to show this. The experiment is a variation of the "Needle in the haystack" test. In this test I placed a riddle in our code, and asked Cursor to solve it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Context experiment
&lt;/h3&gt;

&lt;p&gt;The experiment consisted of a riddle that added a &lt;code&gt;.txt&lt;/code&gt; file of 25k characters ~ &lt;a href="https://www.gptcostcalculator.com/open-ai-token-calculator" rel="noopener noreferrer"&gt;6k tokens&lt;/a&gt; with random text, and a riddle inside the random text. In this case, the riddle was a Caesar cipher with the key at the end of the file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If anyone wants to see the whole experiment, please comment or DM me.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The results of the experiment were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Gemini 2.5 pro
 - Finds the file correctly ❌
 - Gets the answer correct. ❌

* Claude 4.0 sonnet
 - Finds the files correctly ✅
 - Gets the answer right. ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we see, even with only a 6k context window, the Gemini model can't figure out the answer to the riddle. (6k tokens is about ~850 lines of code, &lt;a href="https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them" rel="noopener noreferrer"&gt;4 characters =~ 1 token&lt;/a&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I haven't confirmed, but I am sure that if I made the puzzle more intricate by adding text and more cross-references, the model would break&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is relevant because imagine Cursor looking for how to answer your prompt in the best way possible, it will look online, it will look at all your files, but it won't read them &lt;strong&gt;UNLESS&lt;/strong&gt; you pass the file into context. (You can test this by seeing how many tokens are passed if you use a custom API token in Cursor)&lt;/p&gt;

&lt;h3&gt;
  
  
  How much context do we have?
&lt;/h3&gt;

&lt;p&gt;In Cursor; Gemini, and Claude have 120k tokens for context. That might seem like a lot, but remember our experiment with 6k context? The models could degrade with way shorter contexts.&lt;/p&gt;

&lt;p&gt;Here is the context of a few things for anyone interested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Average stack overflow page call &lt;a href="//./experiments/website-context-experiment.md"&gt;~15k tokens&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The code files in a mid-sized company are at least a few million characters, which is at least 0.75 Million Tokens

&lt;ul&gt;
&lt;li&gt;Average characters per file is about 4,721 characters ~= 1k tokens.&lt;/li&gt;
&lt;li&gt;Large files for an average company are ~= 30k characters ~= 8k tokens.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How Cursor handles a prompt.
&lt;/h1&gt;

&lt;p&gt;This is how the Cursor handles a prompt:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You write your prompt and add your context. Then press enter.&lt;/li&gt;
&lt;li&gt;Cursor uses it's proprietary model to analyze your prompt and context. You haven't hit the AI model yet (Gemini, Claude, etc...)

&lt;ul&gt;
&lt;li&gt;There is no official documentation that says this directly, but there are allusions to it in Cursor official docs, and in Anthropic's system prompt docs. (And the leaked system prompt from Anthropic around May 2025).

&lt;ul&gt;
&lt;li&gt;Cursor prompts some tools with a proprietary LLM or algorithm to decide what MCPs to use.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cursor takes the context you added, AND it takes what it thinks is the relevant context and appends it to the api.

&lt;ul&gt;
&lt;li&gt;Cursor also adds a small system prompt (can't find official docs. But cursor adds about ~1k tokens to all your requests. I assume those are its custom instructions.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The API/LLM then processes the prompt, and you receive the answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  How to improve my prompts? - Keep track of your decisions in a document.
&lt;/h1&gt;

&lt;p&gt;Finally, the good part. Here is what to do if you want to  significantly improve the context that you send Cursor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make one or two documents depending on how big is your task, I usually call it "PLAN.md" and/or "TASKS.md"&lt;/li&gt;
&lt;li&gt;Write down the problem, objective, insights and thoughts you have, and a plan on "PLAN.md", you can use AI to help you write the plan. It's usually useful to ask AI to write a plan with "atomic" steps, so it gives you small steps to follow.&lt;/li&gt;
&lt;li&gt;Start working on the problem, every time you make progess (progress might be a new insight, a new piece of code, etc...), write down what you did on "TASKS.md", it's even better if you can reference what you did at "PLAN.md" too. Then commit. &lt;/li&gt;
&lt;li&gt;Keep going. Remember to write new insights, failed attempts, and sources too in your documents. This development loop forces two things.

&lt;ul&gt;
&lt;li&gt;You will think more about what you are doing, which is a lot of help as a developer.&lt;/li&gt;
&lt;li&gt;The AI will keep close track of context, and you can easily revert and correct if it goes off track. The AI will also be way better at problem-solving.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>programming</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>How to organize your project's folders - A quick guide to repository and folder structures.</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Sun, 15 Oct 2023 15:49:17 +0000</pubDate>
      <link>https://dev.to/gvegacl/how-to-organize-your-projects-folders-a-quick-guide-to-repository-and-folder-structures-5fje</link>
      <guid>https://dev.to/gvegacl/how-to-organize-your-projects-folders-a-quick-guide-to-repository-and-folder-structures-5fje</guid>
      <description>&lt;p&gt;Depending on your objectives and needs, there are many ways of organizing a project in React. But some patterns are considered best practices; here, I'll explain a few.&lt;/p&gt;

&lt;h1&gt;
  
  
  Atomic Design.
&lt;/h1&gt;

&lt;p&gt;Atomic Design is a concept introduced by Brad Frost, and it's a methodology to build consistent and reusable design systems, especially in web interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Atoms
&lt;/h2&gt;

&lt;p&gt;Atoms are the basic building blocks in this methodology. They're the smallest possible components, like form labels, buttons, inputs, or even design tokens such as colors, fonts, etc. Atoms can be abstract and don't necessarily represent the final product, but they help to create a consistent interface and development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Molecules
&lt;/h2&gt;

&lt;p&gt;Molecules are combinations of two or more atoms that form relatively simple components together. An example could be a form label, a text input, and a button combined to make a search form. While atoms can exist in isolation (like a button), molecules find their true meaning when used in conjunction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organisms.
&lt;/h2&gt;

&lt;p&gt;Organisms are relatively complex UI components composed of groups of molecules (and possibly atoms). Examples might include the header of a website with a logo (atom), a navigation menu (molecule), and a search form (molecule). Organisms provide context for the molecules and demonstrate how molecules can work together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/src
  /components
    /atoms
      Button.jsx
      Icon.jsx
      ...
    /molecules
      FormInput.jsx
      UserAvatar.jsx
      ...
    /organisms
      Header.jsx
      Sidebar.jsx
      ...
    /templates
      MainLayout.jsx
      UserLayout.jsx
      ...
    /pages
      HomePage.jsx
      UserProfilePage.jsx
      ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  By feature or Domain.
&lt;/h1&gt;

&lt;p&gt;This structure is popular for large projects. Here, we group components by the feature they relate to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/src
  /components
    /Button
      Button.jsx
      Button.test.jsx
      Button.css
    /Modal
      Modal.jsx
      Modal.test.jsx
      Modal.css
  /features
    /Todo
      /components
        TodoList.jsx
        TodoItem.jsx
      TodoActions.js
      TodoReducer.js
    /User
      /components
        UserProfile.jsx
        UserSettings.jsx
      UserActions.js
      UserReducer.js

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  By File Type
&lt;/h1&gt;

&lt;p&gt;In smaller projects or applications with many shared components, it might make sense to group files by their type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/src
  /components
    Button.jsx
    Modal.jsx
    ...
  /containers
    TodoContainer.jsx
    UserContainer.jsx
    ...
  /actions
    TodoActions.js
    UserActions.js
    ...
  /reducers
    TodoReducer.js
    UserReducer.js
    ...
  /styles
    Button.css
    Modal.css
    ...

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Domain-driven Design (DDD)
&lt;/h1&gt;

&lt;p&gt;For projects that closely align with domain-driven Design, you might structure your folders around aggregates, entities, and value objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/src
  /User
    UserAggregate.js
    UserEntity.js
    UsernameValue.js
    ...
  /Product
    ProductAggregate.js
    ProductEntity.js
    PriceValue.js
    ...

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Modules or Packages
&lt;/h1&gt;

&lt;p&gt;Modules are self-contained units of code that encapsulate a specific functionality or feature, often used in mono repo structures or when scaling with multiple teams.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/src
  /modules
    /Todo
      TodoView.jsx
      TodoLogic.js
      TodoService.js
      ...
    /User
      UserView.jsx
      UserLogic.js
      UserService.js
      ...

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;The takeaway is to keep your structure simple. It should be intuitive for developers to know where to place their files and easy for them to be consistent. &lt;br&gt;
I showed a few common structures in this article, but feel free to use what fits your team.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Job hunting in Canada. (And tips for recent immigrants)</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Sat, 07 Oct 2023 15:46:40 +0000</pubDate>
      <link>https://dev.to/gvegacl/job-hunting-in-canada-and-tips-for-recent-immigrants-mo3</link>
      <guid>https://dev.to/gvegacl/job-hunting-in-canada-and-tips-for-recent-immigrants-mo3</guid>
      <description>&lt;p&gt;I got laid off at the beginning of September 2023. And I was lucky enough to get a job offer from another company two weeks later. I have also worked for over eight companies as a contractor and had to go through the same hoops in each.&lt;/p&gt;

&lt;p&gt;This piece is not a guide to get into big tech, but rather somewhere in the middle-high range where you can grow, have a balance, and still make good money. So here are my five learnings from my experience:&lt;/p&gt;

&lt;h1&gt;
  
  
  Be a good engineer.
&lt;/h1&gt;

&lt;p&gt;The article's most essential and irreplaceable point is that you can't swindle your way around getting a software developer job. You need to read and put in the effort to practice coding challenges.&lt;br&gt;
I recommend a few resources for this: &lt;a href="https://roadmap.sh/" rel="noopener noreferrer"&gt;roadmap.sh&lt;/a&gt; will familiarize you with the concepts you need to learn and provide valuable resources to understand them.&lt;/p&gt;

&lt;p&gt;I also recommend &lt;a href="https://neetcode.io/" rel="noopener noreferrer"&gt;neetcode.io&lt;/a&gt; to try your hand at practicing coding challenges and essential concepts.&lt;/p&gt;

&lt;h1&gt;
  
  
  Apply to everything fast.
&lt;/h1&gt;

&lt;p&gt;This advice is controversial, but this is what works for me and a few other people I've talked with. Our current job market is a mess; Some companies post ghost jobs that they don't intend to fill, [1][2], others fill the position and forget their post, and others are almost done with their recruitment process, so the odds are against you, and others.&lt;/p&gt;

&lt;p&gt;My solution is to apply to everything quickly, sort postings by date, and apply to every job related to what you want, even in companies you don't like.&lt;br&gt;
Now, you either get an opportunity to interview for a company you like or you get practice interviewing with a company you don't like. Remember, you can always turn down an offer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I have heard that this is mean and that you are wasting people's time. This point is debatable, but the short answer is that most companies have their interview process automated, and most companies don't care if they are wasting your time with many rounds of interviews. You should follow your moral compass, but don't feel bad for doing what you must do.&lt;/p&gt;

&lt;p&gt;I usually hear this kind of argument from underrepresented people in tech, especially women. If it helps, most companies want to attract more BIOPC, LGBTQ+, and woman coders, so apply liberally. [3] [4] [5]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where to apply?
&lt;/h2&gt;

&lt;p&gt;I don't like &lt;a href="https://www.linkedin.com/in/cesar-leonardo-vega-galvan/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, but in Canada, there are excellent offers at &lt;a href="https://ca.indeed.com/?r=us" rel="noopener noreferrer"&gt;Indeed.com&lt;/a&gt;, &lt;a href="https://www.levels.fyi/" rel="noopener noreferrer"&gt;Levels.fyi&lt;/a&gt; and my favorite &lt;a href="https://hired.com/" rel="noopener noreferrer"&gt;Hired.com&lt;/a&gt;, a friend also recommended &lt;a href="https://simplify.jobs/" rel="noopener noreferrer"&gt;simplify.jobs&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tips for immigrants
&lt;/h1&gt;

&lt;p&gt;In Canada, it is generally not legal for employers to ask about your immigration status during the hiring process [6]. But if you are requested your immigration status after receiving a job offer, you should disclose it at this point. [7].&lt;/p&gt;

&lt;p&gt;But then again, if you want the job, you will disclose it if they ask. My advice is to be honest, always have your papers in order, and keep your answers short so you don't dig a hole for yourself. If you are worried about this, contact an immigration lawyer.&lt;/p&gt;

&lt;h1&gt;
  
  
  Be patient.
&lt;/h1&gt;

&lt;p&gt;I sent almost 100 applications before getting an offer, and earlier in my career, there were times when I spent months and over 300 applications before getting a job. Be patient and learn what you are comfortable negotiating. Plenty of companies are out there, and work is a big part of the day, so feel free to let one offer slip out if you don't fit in the culture. But don't be too picky, you might realize that you enjoy working at a company after all.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sources:
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://news.yahoo.com/companies-posting-fake-ghost-jobs-111500854.html?guccounter=1&amp;amp;guce_referrer=aHR0cHM6Ly93d3cucGhpbmQuY29tLw&amp;amp;guce_referrer_sig=AQAAABhRAbWL9NTEjsxkobD2LzASiIPOFuoimnYlNxWPwZqLZOh0002SKHLoHq2xtntv1Wx1KS1T4GaY1JxrZsYYEdm0zd5unOeeZRDYaYV6C3EkjJWED_u6Ni_7_xI_WFiA0OVBGvnKQZi7yHTZl1LaQYqO285-5WBVRhLQnNVmctBt" rel="noopener noreferrer"&gt;news.yahoo.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.forbes.com/sites/robinryan/2022/11/30/be-careful-of-employers-posting-ghost-jobs/?sh=14c64700142d" rel="noopener noreferrer"&gt;Forbes.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.forbes.com/sites/committeeof200/2023/02/28/to-attract-more-women-in-tech-the-industry-needs-to-change/?sh=56a9b3ab3628" rel="noopener noreferrer"&gt;Forbes.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hbr.org/2021/12/two-ways-to-improve-gender-balance-in-tech" rel="noopener noreferrer"&gt;hbr.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.forbes.com/sites/forbescommunicationscouncil/2023/04/14/why-tech-companies-should-hire-more-women-and-make-it-a-part-of-their-csr-policy/?sh=5b99df33aac3" rel="noopener noreferrer"&gt;Forbes.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.canadavisa.com/moving-to-canada-from-the-u-s.html" rel="noopener noreferrer"&gt;Canadavisa.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/pulse/so-you-legally-entitled-work-canada-dorothy-keenan/" rel="noopener noreferrer"&gt;LinkedIn.com&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>webdev</category>
      <category>job</category>
    </item>
    <item>
      <title>Fixing (and removing) commits in a git branch for beginners.</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Sun, 01 Oct 2023 14:34:27 +0000</pubDate>
      <link>https://dev.to/gvegacl/fixing-and-removing-commits-in-a-git-branch-for-beginners-4jp9</link>
      <guid>https://dev.to/gvegacl/fixing-and-removing-commits-in-a-git-branch-for-beginners-4jp9</guid>
      <description>&lt;p&gt;We have all been there. You want to merge and have a different set of commits that you want to have. Maybe you rebased a branch you shouldn't have or checked out from the wrong branch. But don't worry, fixing it is easy.&lt;/p&gt;

&lt;p&gt;In this article, I will show a few techniques to get your branch exactly how you want it and avoid merging code and commits that do not belong in your target branch. &lt;/p&gt;

&lt;p&gt;Let's start with an example where we have four commits:&lt;br&gt;
&lt;code&gt;git log --oneline&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c761a8f (HEAD -&amp;gt; my-feature) commit-3
e056796 bad-commit-1
269a02d commit-2
ceaab87 commit-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that a bad commit snuck into our branch, so how do we fix it?&lt;br&gt;
It's simple: First, we make sure the commit is unnecessary and want to remove it.&lt;br&gt;
We can do this with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git show e056796&lt;/code&gt;&lt;br&gt;
or &lt;br&gt;
&lt;code&gt;git diff e056796^!&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And then, we can see what our commit did.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pro tip: This command will look prettier if you have customized your terminal with tools like ohMyZSH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our second step is to take that commit out of our history. I do this by rebasing our branch. We would do an interactive rebase to our target branch in our example.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git rebase -i target-branch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;An editor (often Vim or Nano) will open, showing the last commits. It'll look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pick ceaab87 commit-1
pick 269a02d commit-2
pick e056796 bad-commit-1
pick c761a8f commit-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, to keep the commits we want, delete the line corresponding to the bad commit (bad-commit-1):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
pick ceaab87 commit-1
pick 269a02d commit-2
pick c761a8f commit-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit and save the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pro tip: For Vim, press "i" to interact with the text. And then, you click "ESC" -&amp;gt; "wq" to save and exit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, if you've already pushed your branch to a remote repository before this change, you'll need to &lt;code&gt;force push&lt;/code&gt; your changes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git push origin your-branch-name --force-with-lease&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;And now you have a clean branch without all those pesky commits. Always double-check with your team if those commits are unnecessary, and remember that this will only affect YOUR branch, so don't be too scared to go through the process.&lt;/p&gt;

</description>
      <category>git</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Understanding Coupling in React: Best practices and Examples</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Mon, 25 Sep 2023 19:34:38 +0000</pubDate>
      <link>https://dev.to/gvegacl/understanding-coupling-in-react-practices-and-examples-1h1n</link>
      <guid>https://dev.to/gvegacl/understanding-coupling-in-react-practices-and-examples-1h1n</guid>
      <description>&lt;p&gt;Coupling refers to the degree of interdependence between different modules or classes in a system.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What is Coupling?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Coupling speaks to how closely connected different components or modules are within an application. High coupling means that changes in one module may necessitate changes in many other modules. This results in a codebase that becomes increasingly difficult to maintain and modify over time. As such, best practices usually recommend minimizing coupling.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Separate Components by Responsibility&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A prevalent form of coupling occurs when a single file or component assumes multiple responsibilities (Look at the Single Responsibility Principle). We should avoid patterns where a component takes on multiple roles; For example, having a component determine if it should render as a header or footer based on props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HeaderOrFooter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;isHeader&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isHeader&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;footer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;  &lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, consider creating distinct components for different roles and handling display logic at the parent level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ParentComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nx"&gt;isHeader&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Footer&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Footer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;footer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;  &lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Avoid Prop Drilling&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Prop drilling refers to the practice of passing props from a parent component down through its descendants. If your components are tightly intertwined with a shared state, it's a clear sign of tight coupling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ParentComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProp1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProp2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProp3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;IChildComponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GrandChildComponent&lt;/span&gt; &lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To combat this, you can use shared context or architect your components with a clearer separation of concerns. Remember to use context carefully to avoid bloating the global state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ParentComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProp1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProp2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProp3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;....&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GrandChildComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GrandChildComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SharedContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="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;
  
  
  &lt;strong&gt;Fetch Data Close to Where It's Used&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is more of a personal preference, but I like fetching and managing data as close to its usage point as possible. This helps reduce unnecessary props passed down and keeps data handling within the concerned component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ParentComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGetData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A better approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ParentComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ChildComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGetData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;In conclusion, managing coupling is essential for scalable and maintainable applications. By separating concerns, avoiding prop drilling, and fetching data where it's used, we can significantly reduce the effects of high coupling and build more robust React applications.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>programming</category>
    </item>
    <item>
      <title>useContext to manage state between React Sibling Components.</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Tue, 12 Sep 2023 14:47:26 +0000</pubDate>
      <link>https://dev.to/gvegacl/usecontext-to-manage-state-between-react-sibling-components-3apj</link>
      <guid>https://dev.to/gvegacl/usecontext-to-manage-state-between-react-sibling-components-3apj</guid>
      <description>&lt;p&gt;Have you ever wanted two separate components that share common state? Maybe a controller component and a display component where one component deals with the controls and the other with displaying it. Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Box&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid gap-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1fr 1fr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GameGrid&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GameControls&lt;/span&gt; &lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;setC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setC&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Box&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might be okay if we just pass a couple props, but even with few props it feels wrong. So, how do we do it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Share context between sibling components.
&lt;/h2&gt;

&lt;p&gt;To do this you need to create a context object and wrap the components in it. And then you can get the context in each object, regardless of where you manage the sate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GameContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IGameControls&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
  &lt;span class="p"&gt;....&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;contextValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IGameControls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;setB&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GameContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;contextValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Box&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid gap-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1fr 1fr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GameGrid&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GameControls&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Box&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/GameContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One important thing to pay attention to is that &lt;strong&gt;we only share this state between the sibling components&lt;/strong&gt;, so the rest of the code is not burdened by this data.&lt;/p&gt;

&lt;p&gt;And now we can get the context in each component easily:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const { a, setA, .... } = useGameContext();&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra sources:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/53648661/how-to-share-state-between-child-component-siblings-in-reactjs" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/53648661/how-to-share-state-between-child-component-siblings-in-reactjs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/sharing-state-between-components" rel="noopener noreferrer"&gt;https://react.dev/learn/sharing-state-between-components&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>designpatterns</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Next.js server prop did not match client prop; And conditional style/class rendering</title>
      <dc:creator>Cesar Vega</dc:creator>
      <pubDate>Mon, 11 Sep 2023 22:01:11 +0000</pubDate>
      <link>https://dev.to/gvegacl/nextjs-server-prop-did-not-match-client-prop-and-conditional-styleclass-rendering-2i4c</link>
      <guid>https://dev.to/gvegacl/nextjs-server-prop-did-not-match-client-prop-and-conditional-styleclass-rendering-2i4c</guid>
      <description>&lt;p&gt;Have you ever gotten the enigmatic Next.js error: "Warning: Prop "{your-prop}" did not match. Server: "{value-1}" Client: "{value-2}"?&lt;/p&gt;

&lt;p&gt;Here is the fun fact, conditional rendering did not cause this error. But at the beginning I thought it did, that's why I created this post, so everyone struggling to get rid of that error can understand why it happens, and how to solve it.&lt;/p&gt;

&lt;p&gt;Here is our example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TGrid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[][];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateRandomTiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TGrid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;numRows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numCols&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt; &lt;span class="c1"&gt;// returns a live cell 70% of the time&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setGrid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TGrid&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generateRandomTiles&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
              &lt;span class="c1"&gt;// classNames is a library that combines class texts. But this can be thought as "w-4 h-4 border-solid border border-gray-400" + grid[i][k] ? "bg-pink-400" : "bg-white"&lt;/span&gt;
              &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;classNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w-4 h-4 border-solid border border-gray-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;k&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;bg-pink-400&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;bg-white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="p"&gt;)}&lt;/span&gt;
              &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&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="nx"&gt;i&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="nx"&gt;k&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This code causes the following error to fire:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: Prop &lt;code&gt;className&lt;/code&gt; did not match. Server: "w-4 h-4 border-solid border border-gray-400 bg-white" Client: "w-4 h-4 border-solid border border-gray-400 bg-pink-400"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I thought it happened because I conditionally rendered &lt;code&gt;bg-pink-400&lt;/code&gt; and &lt;code&gt;bg-white&lt;/code&gt;. But it wasn't, the real problem is a little more sneaky than that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not generate values when defining useState.
&lt;/h2&gt;

&lt;p&gt;The real culprit is this the line where we define our grid state:&lt;br&gt;
&lt;code&gt;const [grid, setGrid] = useState&amp;lt;TGrid&amp;gt;(generateRandomTiles());&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The problem here is that Next.js has certain state in mind when we generate the tiles in the server, but when the client runs hydrates, we end up with a different random array when we generateRandomTiles() in the frontend. You can notice this if you console log grid, and check your next.js terminal log and your frontend log.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is a great article by react.gg about this: &lt;a href="https://ui.dev/c/react/effects?ck_subscriber_id=2248853627" rel="noopener noreferrer"&gt;https://ui.dev/c/react/effects?ck_subscriber_id=2248853627&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  How do we fix it?
&lt;/h2&gt;

&lt;p&gt;Simple, we manage effects on the frontend. Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setGrid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TGrid&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialGrid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateRandomTiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Comment, first refresh&lt;/span&gt;
    &lt;span class="nf"&gt;setGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialGrid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now we will not have a mismatch between frontend and backend style rendering, and the error will not happen anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links if you want to go deeper:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/70093133/add-dynamic-tailwind-class-to-a-react-component-next-js-tailwind-ts" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/70093133/add-dynamic-tailwind-class-to-a-react-component-next-js-tailwind-ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/69523463/nextjs-not-render-as-expected-and-throw-warning-prop-style-did-not-match-wh" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/69523463/nextjs-not-render-as-expected-and-throw-warning-prop-style-did-not-match-wh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tailwindlabs/tailwindcss/discussions/3461" rel="noopener noreferrer"&gt;https://github.com/tailwindlabs/tailwindcss/discussions/3461&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>tailwindcss</category>
      <category>css</category>
      <category>react</category>
    </item>
  </channel>
</rss>
