<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: ASafaeirad</title>
    <description>The latest articles on Forem by ASafaeirad (@asafaeirad).</description>
    <link>https://forem.com/asafaeirad</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F554506%2Fdbb3e043-2a35-4b0a-b414-956c0ffd5a12.png</url>
      <title>Forem: ASafaeirad</title>
      <link>https://forem.com/asafaeirad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/asafaeirad"/>
    <language>en</language>
    <item>
      <title>Why CSS Is So Hard for Generative AIs to Understand?</title>
      <dc:creator>ASafaeirad</dc:creator>
      <pubDate>Sun, 09 Nov 2025 21:12:57 +0000</pubDate>
      <link>https://forem.com/asafaeirad/why-css-is-so-hard-for-generative-ais-to-understand-17fo</link>
      <guid>https://forem.com/asafaeirad/why-css-is-so-hard-for-generative-ais-to-understand-17fo</guid>
      <description>&lt;p&gt;CSS is one of those things that looks simple, until you actually try to reason about it. Most people consider CSS an easy language to learn, yet somehow, LLMs that can solve calculus problems still get defeated by a few lines of CSS.&lt;/p&gt;

&lt;p&gt;In this article, I'll share my thoughts on why Generative AIs struggle to create good layouts, and how &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt; helps them overcome this challenge.&lt;/p&gt;

&lt;p&gt;Let's imagine we're using CSS the way the web originally intended, with a separate CSS file, defined selectors, and a linked stylesheet. In this setup, let's discuss the CSS nature.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Everything Depends on Context
&lt;/h3&gt;

&lt;p&gt;In CSS a single rule like &lt;code&gt;position: relative&lt;/code&gt; can completely alter the layout and even all other properties behavior. So one line might behave in many different ways, depending on where it used.&lt;/p&gt;

&lt;p&gt;By design, it's impossible to look at a single part of a CSS file and predict exactly what you'll see in the browser.&lt;/p&gt;

&lt;p&gt;For any layout property, you need to understand its &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Positioned_layout/Stacking_context#the_stacking_context" rel="noopener noreferrer"&gt;stacking context&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Display/Containing_block" rel="noopener noreferrer"&gt;containing block&lt;/a&gt;, &lt;a href="https://www.joshwcomeau.com/css/understanding-layout-algorithms/" rel="noopener noreferrer"&gt;layout mode&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/writing-mode" rel="noopener noreferrer"&gt;writing-mode&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/direction" rel="noopener noreferrer"&gt;direction&lt;/a&gt;, and the list goes on.&lt;/p&gt;

&lt;p&gt;These hidden relationships are so complex that many developers aren't even aware of them. We often end up "fixing" the layout by trial and error. Playing with properties until something finally works.&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%2Fx6tbnp4ngqztvzawmp9a.gif" 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%2Fx6tbnp4ngqztvzawmp9a.gif" alt="a dev fixing a layout problem" width="320" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's make it even worse!&lt;/p&gt;

&lt;h3&gt;
  
  
  2. CSS Never Tells the Whole Story
&lt;/h3&gt;

&lt;p&gt;A CSS file only gives you half the context.&lt;/p&gt;

&lt;p&gt;You can read every selector and property you want, but until you see the HTML, you have no idea how those styles actually connect to real elements.&lt;/p&gt;

&lt;p&gt;It's like trying to understand a movie by reading its unordered subtitles. the information is there, but completely out of context. Or even worse. Because of the cascading nature of CSS, even the parts you think you know might change later.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. There's No One True Way to Write CSS
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Please read the next paragraph carefully, every word matters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CSS is an &lt;strong&gt;ever-evolving, declarative, cascading language&lt;/strong&gt;, &lt;strong&gt;shaped by an open community&lt;/strong&gt;, and implemented by browsers that each have &lt;strong&gt;their own priorities&lt;/strong&gt; and &lt;strong&gt;can't afford breaking changes&lt;/strong&gt;. It's not &lt;strong&gt;abstract enough to express a the layout intents directly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Which is why &lt;a href="https://www.joshwcomeau.com/css/center-a-div/" rel="noopener noreferrer"&gt;there are so many ways to center a div&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond the countless ways to achieve the same result, there are just as many methodologies for writing CSS itself. Have you ever came across &lt;a href="https://getbem.com/" rel="noopener noreferrer"&gt;BEM&lt;/a&gt;, &lt;a href="https://smacss.com/" rel="noopener noreferrer"&gt;SMACSS&lt;/a&gt;, &lt;a href="https://www.smashingmagazine.com/2011/12/an-introduction-to-object-oriented-css-oocss/" rel="noopener noreferrer"&gt;OOCSS&lt;/a&gt;, &lt;a href="https://css-tricks.com/lets-define-exactly-atomic-css/" rel="noopener noreferrer"&gt;atomic CSS&lt;/a&gt;, etc. If not, then you can add yours to the list.&lt;/p&gt;

&lt;p&gt;The endless mix of techniques, conventions, and methodologies means there's no universal way to reason about CSS across projects.&lt;br&gt;
Every team has its own flavor, and that adds to the chaos specially for LLMs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chaos
&lt;/h3&gt;

&lt;p&gt;Now, combine all three problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context-dependent behavior&lt;/li&gt;
&lt;li&gt;Missing half the story&lt;/li&gt;
&lt;li&gt;No consistent structure or conventions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CSS becomes an unpredictable puzzle of dependencies, overrides, and invisible context. For a language model that relies on clear patterns and relationships "it's a nightmare".&lt;/p&gt;

&lt;p&gt;But GenAIs are not that bad. Are they?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Underrated Contribution of Tailwind
&lt;/h3&gt;

&lt;p&gt;In my opinion, Tailwind CSS is the underrated hero of the AI era.&lt;br&gt;
It brings order to the madness and gives machines a predictable way to reason about styling. Here's why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Colocation of structure and style&lt;/strong&gt;: Giving full context in one place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opinionated&lt;/strong&gt;: a small, consistent set of utilities means no guessing or inventing conventions. That can shape repeating patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable output&lt;/strong&gt;: each class does one thing, with no unexpected cascade effects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That predictability is exactly what makes Tailwind LLM-friendly.&lt;br&gt;
So next time you ask an AI to build you a webpage, just say "use Tailwind."&lt;br&gt;
Chances are, it was going to do that anyway. 😄&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Update
&lt;/h2&gt;

&lt;p&gt;PS: I'm not promoting Tailwind or claiming it's better than other approaches. I’m just sharing some observations about why it works so well for AI models&lt;/p&gt;

</description>
      <category>llm</category>
      <category>css</category>
      <category>ai</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>The Myth of GraphQL</title>
      <dc:creator>ASafaeirad</dc:creator>
      <pubDate>Tue, 15 Oct 2024 18:45:25 +0000</pubDate>
      <link>https://forem.com/asafaeirad/the-myth-of-graphql-20fl</link>
      <guid>https://forem.com/asafaeirad/the-myth-of-graphql-20fl</guid>
      <description>&lt;p&gt;It's often said that GraphQL fixes the problems of &lt;strong&gt;under-fetching&lt;/strong&gt; and &lt;strong&gt;over-fetching&lt;/strong&gt;. But is that really the case? In theory, it sounds promising. In practice, however, you might be trading problems for a pile of new ones that even the most sophisticated frameworks struggle to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Temptation to Put Everything in a Single Request
&lt;/h2&gt;

&lt;p&gt;Imagine you're at an all-you-can-eat buffet, and someone advises you, &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just load up your plate with everything you might possibly want in one go; it'll save you trips!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sounds efficient, right? That's akin to what GraphQL suggests: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pack as much data as you need into a single request to avoid under-fetching, and I'll let you specify exactly what you want to prevent over-fetching.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's follow the advice! &lt;br&gt;
To achieve this in a React application, we might hoist our data-fetching logic up to the highest level and pass down this huge data object to our presentational components.&lt;/p&gt;

&lt;p&gt;Here is an example data schema we get for a query using &lt;a href="https://hasura.io/" rel="noopener noreferrer"&gt;Hasura&lt;/a&gt; and &lt;a href="https://the-guild.dev/graphql/codegen" rel="noopener noreferrer"&gt;GraphQL-Codegen&lt;/a&gt;&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query_root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;project_by_pk&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;project&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SchemaTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectStatusEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;start_date&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;due_date&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;households&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;household_project&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;household&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;household&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SchemaTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HouseholdStatusEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SchemaTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HouseholdSeverityEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;members_count&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, instead of neat, modular components with their own data queries, we have a massive, monolithic object—with an &lt;strong&gt;ad-hoc schema full of random nulls&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 1: We've just sacrificed co-location! But on the bright side, we have presentational components instead 🎉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Quest for a Meaningful Schema
&lt;/h2&gt;

&lt;p&gt;You're right to say: "Why is the schema ad-hoc? it's a skill issue. Can't we make some meaningful entities?"&lt;br&gt;
One approach is to create fragments like &lt;code&gt;ProjectStatusFragment&lt;/code&gt;, &lt;code&gt;HouseholdIdentityFragment&lt;/code&gt;, and &lt;code&gt;HouseholdMembersFragment&lt;/code&gt;, and enforce their usage across the team.&lt;/p&gt;

&lt;p&gt;But wait—do we need all the data behind these fragments every time? Fragments are meant to be reusable, but reusability can lead to over-fetching, which contradicts GraphQL's main promise:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Query exactly what you need on the client—no more, no less.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the real world, use cases are infinite. To create meaningful fragments without overfetching, we'd need to create an infinite number of fragments. That's neither practical nor efficient. So we default back to flexible schemas, letting each use case decide what data it needs.&lt;/p&gt;

&lt;p&gt;This leads us back to square one with a lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every abstraction layer and reusability introduces data over-fetching, contradicting GraphQL's core promise.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The Problem of Nulls
&lt;/h2&gt;

&lt;p&gt;Why are there so many random nulls in our data? The answer lies in &lt;a href="https://graphql.org/learn/best-practices/#nullability" rel="noopener noreferrer"&gt;GraphQL's design decisions regarding nullability&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In GraphQL, every field and every type is nullable by default. ... By defaulting every field to nullable, any of field failure may result in just that field returning "null" rather than having a complete failure for the request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means our schemas are riddled with optional fields, leading to a data structure filled with nulls. It's not necessarily a bad design choice, but it's the reality when we work with GraphQL.&lt;/p&gt;
&lt;h2&gt;
  
  
  Returning to the Core Issue
&lt;/h2&gt;

&lt;p&gt;Now, without any skill issues, we're stuck with a massive data with an ad-hoc and partial schema. We need to pass this data to our presentational components, but how?&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 1: Prop Drilling
&lt;/h3&gt;

&lt;p&gt;One option is prop drilling. But is it practical to pass such a data schema without losing our sanity? Not really.&lt;/p&gt;

&lt;p&gt;Consider the purpose of presentational components: they are &lt;strong&gt;free of side effects&lt;/strong&gt;, &lt;strong&gt;loosely coupled&lt;/strong&gt;, and therefore &lt;strong&gt;reusable&lt;/strong&gt; and &lt;strong&gt;easy to test&lt;/strong&gt;. By passing down this enormous, loosely typed object, we're tightly coupling our components to a specific query structure.&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;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;households&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;household_project&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;household&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;household&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SchemaTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HouseholdStatusEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SchemaTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HouseholdSeverityEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;members_count&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;HouseholdList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;households&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tight dependency isn't just about what a component uses or imports. In software development, dependency means &lt;strong&gt;"What information is this part of the code aware of?"&lt;/strong&gt; When a piece of code is aware of specific information, it becomes responsible for reacting whenever that information changes. This means our &lt;code&gt;HouseholdList&lt;/code&gt; component isn't just using the data; it is coupled to the exact structure of our query results. As a result, any change in the query triggers a change in our component's high-level API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it tightly coupled?&lt;/strong&gt; Absolutely.&lt;br&gt;
&lt;strong&gt;Is it easy to test?&lt;/strong&gt; Not at all.&lt;/p&gt;

&lt;p&gt;Presentational components aren't free. They depend on their parent components to handle responsibilities and side effects like data fetching. By shifting this responsibility away from the components themselves, we introduce duplication. Every time we reuse these components in different contexts, we have to replicate the same data-fetching logic in their parent components.&lt;/p&gt;

&lt;p&gt;In this scenario, we get the worst of both worlds: we don't reap the benefits of presentational components, but we still pay the costs.&lt;/p&gt;

&lt;p&gt;And let's not forget, our data is littered with nulls. The bigger question is &lt;strong&gt;should our components accept nullable values just because our I/O isn't reliable?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Here's the next lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Passing a raw query result as props, couples our components to the unpredictability of I/O.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Searching for Meaningful Interfaces
&lt;/h3&gt;

&lt;p&gt;To untangle this mess, we might try to create meaningful, decoupled interfaces. We'll map our unwieldy data to what each component needs, embracing abstraction.&lt;/p&gt;

&lt;p&gt;But here's the kicker: good abstraction clashes with the &lt;em&gt;just query what you need&lt;/em&gt; approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's attempt to create a &lt;code&gt;Project&lt;/code&gt; entity and a mapper function:&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;Project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toProject&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="nx"&gt;X&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what is &lt;code&gt;X&lt;/code&gt;? If we assume it's the generated &lt;code&gt;Project&lt;/code&gt; type from our GraphQL schema, we're in trouble. Consider this query:&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`{ projects { id, dueDate } }`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This data lacks the fields needed to map to our &lt;code&gt;Project&lt;/code&gt; entity. We can't reliably map partial data to a full entity without risking runtime errors or inconsistent state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Using Context
&lt;/h3&gt;

&lt;p&gt;Okay, maybe crafting meaningful interfaces is off the table, but we can prevent our component interfaces from getting polluted by avoiding prop drilling altogether. &lt;strong&gt;"Aha! We'll use React's Context API!"&lt;/strong&gt; We set up a provider and pass our data through context!&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;Page&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="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePageQuery&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;MyProvider&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;query&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;MyChildren&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;/MyProvider&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyChildren&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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyProvider&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;But hold on—aren't we just coupling &lt;code&gt;MyChildren&lt;/code&gt; to &lt;code&gt;usePageQuery&lt;/code&gt; via context? It's not transparent as we are doing it via dependency injection but we'll get to that in a second. We have a bigger problem, since &lt;code&gt;ApolloClient&lt;/code&gt; provides a cache with &lt;code&gt;ApolloProvider&lt;/code&gt;, we're adding redundant layers here.&lt;/p&gt;

&lt;p&gt;Simplifying our code, we might write:&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;Page&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="nf"&gt;usePageQuery&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyChildren&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;MyChildren&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePageQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;fetchPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cache-only&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// Component logic&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you see me! Context doesn't solve our fundamental problem; it just obscures it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge of Render-As-You-Fetch
&lt;/h2&gt;

&lt;p&gt;In many cases, we don't need all the data upfront to start rendering.  When we combine everything into one huge request, we make it harder to render parts of our application as soon as their data arrives.&lt;/p&gt;

&lt;p&gt;Yes, we can use directives like &lt;code&gt;@defer&lt;/code&gt;, but implementing them adds layers of complexity to both the client and server.&lt;/p&gt;

&lt;p&gt;Additionally, sometimes, we need different strategies for different data. For instance, we might want to render part of the data on the server and the rest on the client. In this case, we need to break our query into at least two separate queries. (Did I just miss dynamic and static data 🤔)&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;Page&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="nx"&gt;serverQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useServerPageQuery&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;clientQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useClientPageQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cache Invalidation: The Hidden Beast
&lt;/h2&gt;

&lt;p&gt;When we mutate data, we need to update our cache. Sometimes, optimistic updates and manual cache manipulation aren't feasible. The safest route is often to refetch.&lt;/p&gt;

&lt;p&gt;But with our all-in-one query, refetching means fetching the entire dataset again—a heavy, inefficient operation.&lt;/p&gt;

&lt;p&gt;Is there a solution? Perhaps, but it would require sophisticated infrastructure that goes beyond what most app developers should implement. We're talking about systems that can intelligently manage partial cache invalidation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost of Chasing Zero Over-Fetching and Under-Fetching
&lt;/h2&gt;

&lt;p&gt;Let's tally up the costs of striving for zero over-fetching and under-fetching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Coupled Presentational Components&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Co-location&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Signal-to-Noise Ratio&lt;/strong&gt;: Massive generated types and null handling clutter our codebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex Render Strategies&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Management Nightmares&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzptf55pfikwwnzjsaevc.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%2Fzptf55pfikwwnzjsaevc.png" alt="thanos meme" width="742" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Is it worth it?&lt;/p&gt;

&lt;h2&gt;
  
  
  A Reality Check
&lt;/h2&gt;

&lt;p&gt;In practice, many teams abandon the ideal of crafting minimal, all-encompassing queries. Instead, they opt for smaller, reusable data-fetching hooks like &lt;code&gt;useUser&lt;/code&gt;, &lt;code&gt;useComments&lt;/code&gt;, and &lt;code&gt;useWhatever&lt;/code&gt;. They also leverage fragments to promote reusability and define cohesive entities within their GraphQL schemas.&lt;/p&gt;

&lt;p&gt;But wasn't GraphQL's main selling point that it's a query language for the client—allowing us to request data in exactly the shape we need? Yet, in practice, we're using it more like a simple SDK, making straightforward data requests. Aren't we just replicating what could be achieved with RPC or REST calls, but with added complexity?&lt;/p&gt;

&lt;p&gt;And yes, I recognize that GraphQL isn't inherently bad—it does solve certain problems more effectively than other solutions. It offers flexibility, strong typing, and a unified interface for data fetching. However, as app developers, I believe it's time to rethink what we truly gain from using GraphQL before adopting it.&lt;/p&gt;

&lt;p&gt;If you're a tech giant like Facebook, equipped to build and maintain the sophisticated frameworks required to harness GraphQL's full potential, then by all means, leverage it.&lt;/p&gt;

&lt;p&gt;However, for most small to medium-sized enterprises, adopting GraphQL without the necessary resources leads to complexity and frustration. Based on my experience, it often results in a tangled mess rather than streamlined data management.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
