<?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: Hamed Farazi</title>
    <description>The latest articles on DEV Community by Hamed Farazi (@hamedfarazi).</description>
    <link>https://dev.to/hamedfarazi</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%2F4067999%2F9c7fd54d-faf4-4322-9ee3-2f6a69327307.jpg</url>
      <title>DEV Community: Hamed Farazi</title>
      <link>https://dev.to/hamedfarazi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hamedfarazi"/>
    <language>en</language>
    <item>
      <title>Stop Building Websites for Google. Start Building Them for AI</title>
      <dc:creator>Hamed Farazi</dc:creator>
      <pubDate>Wed, 19 Aug 2026 10:53:41 +0000</pubDate>
      <link>https://dev.to/hamedfarazi/stop-building-websites-for-google-start-building-them-for-ai-397</link>
      <guid>https://dev.to/hamedfarazi/stop-building-websites-for-google-start-building-them-for-ai-397</guid>
      <description>&lt;p&gt;For years, we were told to build websites for search engines.&lt;/p&gt;

&lt;p&gt;Add the right keywords.&lt;/p&gt;

&lt;p&gt;Write a good title.&lt;/p&gt;

&lt;p&gt;Generate a sitemap.&lt;/p&gt;

&lt;p&gt;Add structured data.&lt;/p&gt;

&lt;p&gt;Build backlinks.&lt;/p&gt;

&lt;p&gt;Improve Core Web Vitals.&lt;/p&gt;

&lt;p&gt;And, of course, make sure Google can crawl everything.&lt;/p&gt;

&lt;p&gt;That worked.&lt;/p&gt;

&lt;p&gt;But the way people discover information is changing.&lt;/p&gt;

&lt;p&gt;Today, someone might not search for your website at all.&lt;/p&gt;

&lt;p&gt;They might ask ChatGPT.&lt;/p&gt;

&lt;p&gt;Or Claude.&lt;/p&gt;

&lt;p&gt;Or Gemini.&lt;/p&gt;

&lt;p&gt;Or another AI system.&lt;/p&gt;

&lt;p&gt;And that's where things get interesting.&lt;/p&gt;

&lt;p&gt;Because a website can be perfectly optimized for Google and still be &lt;strong&gt;terrible for AI to understand&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The New Problem Isn't Just SEO
&lt;/h2&gt;

&lt;p&gt;Imagine you have a website with 10,000 pages.&lt;/p&gt;

&lt;p&gt;Google can crawl it.&lt;/p&gt;

&lt;p&gt;Your Lighthouse score is great.&lt;/p&gt;

&lt;p&gt;Your sitemap is valid.&lt;/p&gt;

&lt;p&gt;Your metadata looks perfect.&lt;/p&gt;

&lt;p&gt;You have structured data.&lt;/p&gt;

&lt;p&gt;From an SEO perspective, everything looks healthy.&lt;/p&gt;

&lt;p&gt;Now ask an AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What does this company actually do?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer might still be incomplete.&lt;/p&gt;

&lt;p&gt;Or worse, the AI might misunderstand the business entirely.&lt;/p&gt;

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

&lt;p&gt;Because &lt;strong&gt;being crawlable isn't the same as being understandable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the problem I became interested in.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Does "AI-Friendly" Actually Mean?
&lt;/h1&gt;

&lt;p&gt;I started thinking about a website from the perspective of an AI system rather than a search engine.&lt;/p&gt;

&lt;p&gt;An AI needs to answer questions about your website.&lt;/p&gt;

&lt;p&gt;That means it needs to understand things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is this website?&lt;/li&gt;
&lt;li&gt;Who is it for?&lt;/li&gt;
&lt;li&gt;What does this company offer?&lt;/li&gt;
&lt;li&gt;Which pages are important?&lt;/li&gt;
&lt;li&gt;How are different pieces of content related?&lt;/li&gt;
&lt;li&gt;Which information is authoritative?&lt;/li&gt;
&lt;li&gt;Can important facts be extracted reliably?&lt;/li&gt;
&lt;li&gt;Is the content structured consistently?&lt;/li&gt;
&lt;li&gt;Are there conflicting or ambiguous signals?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't exactly SEO.&lt;/p&gt;

&lt;p&gt;It's closer to &lt;strong&gt;machine-readable communication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And that requires a different mindset.&lt;/p&gt;




&lt;h1&gt;
  
  
  So I Built an Experiment
&lt;/h1&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How well optimized is this website for Google?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How understandable is this website to AI?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That led me to build an AI Visibility analysis system.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Website
   ↓
Crawler
   ↓
Page Analysis
   ↓
Content Analysis
   ↓
Schema Analysis
   ↓
Internal Link Analysis
   ↓
AI Optimization Analysis
   ↓
AI Visibility Score
   ↓
Actionable Recommendations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that this isn't based on a few manually selected pages.&lt;/p&gt;

&lt;p&gt;The system starts with the actual website.&lt;/p&gt;

&lt;p&gt;It crawls the real pages.&lt;/p&gt;

&lt;p&gt;Then analyzes what is actually there.&lt;/p&gt;




&lt;h1&gt;
  
  
  Crawling Is Only the Beginning
&lt;/h1&gt;

&lt;p&gt;A crawler can tell you that a page exists.&lt;/p&gt;

&lt;p&gt;That's not enough.&lt;/p&gt;

&lt;p&gt;For every page, I wanted to understand several dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content
&lt;/h3&gt;

&lt;p&gt;Is the content clear?&lt;/p&gt;

&lt;p&gt;Does the page actually explain what it is about?&lt;/p&gt;

&lt;p&gt;Are important entities and concepts explicit?&lt;/p&gt;

&lt;h3&gt;
  
  
  Structure
&lt;/h3&gt;

&lt;p&gt;Are headings meaningful?&lt;/p&gt;

&lt;p&gt;Is the document hierarchy logical?&lt;/p&gt;

&lt;p&gt;Can an AI distinguish the main topic from navigation, boilerplate and unrelated content?&lt;/p&gt;

&lt;h3&gt;
  
  
  Structured Data
&lt;/h3&gt;

&lt;p&gt;Does the page expose useful schema?&lt;/p&gt;

&lt;p&gt;Is the schema actually consistent with the visible content?&lt;/p&gt;

&lt;p&gt;Are important entities represented?&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal Links
&lt;/h3&gt;

&lt;p&gt;Can an AI understand the relationship between pages?&lt;/p&gt;

&lt;p&gt;Or is the website essentially a collection of disconnected documents?&lt;/p&gt;

&lt;h3&gt;
  
  
  Metadata
&lt;/h3&gt;

&lt;p&gt;Titles and descriptions still matter.&lt;/p&gt;

&lt;p&gt;But they're only one signal.&lt;/p&gt;

&lt;p&gt;The bigger question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the metadata agree with the actual content?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The Interesting Part: Scoring
&lt;/h1&gt;

&lt;p&gt;Once you have all this information, you need a way to turn it into something useful.&lt;/p&gt;

&lt;p&gt;So instead of returning a giant technical report, I wanted to produce a score.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Visibility Score
-------------------

Overall Score       85/100

Content Clarity     91
Schema Health       88
Internal Linking    79
Machine Readability 87
AI Optimization     82
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact score isn't the important part.&lt;/p&gt;

&lt;p&gt;The important part is that the score should be &lt;strong&gt;explainable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A number without evidence isn't very useful.&lt;/p&gt;




&lt;h1&gt;
  
  
  Every Score Should Have Evidence
&lt;/h1&gt;

&lt;p&gt;One of the things I care about most in this system is traceability.&lt;/p&gt;

&lt;p&gt;If the system says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Internal Linking: 79&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't want the user to just trust an algorithm.&lt;/p&gt;

&lt;p&gt;I want to show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which pages were analyzed&lt;/li&gt;
&lt;li&gt;which links were found&lt;/li&gt;
&lt;li&gt;which relationships are weak&lt;/li&gt;
&lt;li&gt;what rules were triggered&lt;/li&gt;
&lt;li&gt;and why the score changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same applies to content quality and structured data.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Your schema is bad."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system should be able to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"These 4 pages declare Organization schema, but the Organization entity isn't consistently represented across the site."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's much more actionable.&lt;/p&gt;




&lt;h1&gt;
  
  
  From Analysis to &lt;code&gt;llms.txt&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Then I reached another interesting question.&lt;/p&gt;

&lt;p&gt;If we're trying to make websites easier for AI systems to understand, shouldn't websites explicitly communicate their structure to AI?&lt;/p&gt;

&lt;p&gt;That's where &lt;code&gt;llms.txt&lt;/code&gt; becomes interesting.&lt;/p&gt;

&lt;p&gt;The concept is straightforward:&lt;/p&gt;

&lt;p&gt;Instead of making an AI discover everything from thousands of pages, provide a curated machine-readable entry point.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Example Company

&amp;gt; Example Company provides developer tools for...

## Products

- Product A
  https://example.com/product-a

- Product B
  https://example.com/product-b

## Documentation

- Getting Started
  https://example.com/docs/getting-started

- API Reference
  https://example.com/docs/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't to replace the website.&lt;/p&gt;

&lt;p&gt;It's to give machines a &lt;strong&gt;map&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  But There's a Bigger Shift Happening
&lt;/h1&gt;

&lt;p&gt;This isn't really about &lt;code&gt;llms.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's not even really about AI crawlers.&lt;/p&gt;

&lt;p&gt;It's about a fundamental change in how websites are consumed.&lt;/p&gt;

&lt;p&gt;For a long time, the primary question was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can a search engine find my page?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Increasingly, another question matters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can an AI understand my website well enough to represent it correctly?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are different problems.&lt;/p&gt;




&lt;h1&gt;
  
  
  SEO Isn't Dead
&lt;/h1&gt;

&lt;p&gt;I don't think SEO is going away.&lt;/p&gt;

&lt;p&gt;Quite the opposite.&lt;/p&gt;

&lt;p&gt;A technically healthy website still needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;crawlability&lt;/li&gt;
&lt;li&gt;indexability&lt;/li&gt;
&lt;li&gt;good performance&lt;/li&gt;
&lt;li&gt;useful content&lt;/li&gt;
&lt;li&gt;semantic HTML&lt;/li&gt;
&lt;li&gt;structured data&lt;/li&gt;
&lt;li&gt;internal linking&lt;/li&gt;
&lt;li&gt;strong information architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But these things are becoming part of something larger.&lt;/p&gt;

&lt;p&gt;Think of it 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;                 Website
                    │
        ┌───────────┴───────────┐
        ↓                       ↓
     Search                  AI Systems
        │                       │
       SEO                AI Visibility
        │                       │
        └───────────┬───────────┘
                    ↓
          Machine Understanding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't to choose between Google and AI.&lt;/p&gt;

&lt;p&gt;The goal is to build websites that &lt;strong&gt;machines can understand&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The New Optimization Layer
&lt;/h1&gt;

&lt;p&gt;I think we're moving toward a stack that looks 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;Traditional Web
      ↓
Technical SEO
      ↓
Semantic Structure
      ↓
Structured Data
      ↓
Machine Readability
      ↓
AI Visibility
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the interesting thing is that most websites are still somewhere around the middle.&lt;/p&gt;

&lt;p&gt;They were designed for humans.&lt;/p&gt;

&lt;p&gt;Then optimized for search engines.&lt;/p&gt;

&lt;p&gt;But they weren't necessarily designed to be interpreted by language models.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Learned Building This
&lt;/h1&gt;

&lt;p&gt;The biggest lesson wasn't about AI.&lt;/p&gt;

&lt;p&gt;It was about abstraction.&lt;/p&gt;

&lt;p&gt;A website contains an enormous amount of information.&lt;/p&gt;

&lt;p&gt;Humans can navigate it intuitively.&lt;/p&gt;

&lt;p&gt;Machines need signals.&lt;/p&gt;

&lt;p&gt;Those signals can come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URLs&lt;/li&gt;
&lt;li&gt;headings&lt;/li&gt;
&lt;li&gt;semantic HTML&lt;/li&gt;
&lt;li&gt;metadata&lt;/li&gt;
&lt;li&gt;schema&lt;/li&gt;
&lt;li&gt;links&lt;/li&gt;
&lt;li&gt;content hierarchy&lt;/li&gt;
&lt;li&gt;consistent entities&lt;/li&gt;
&lt;li&gt;explicit relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When these signals agree with each other, the website becomes much easier to understand.&lt;/p&gt;

&lt;p&gt;When they contradict each other, even a powerful AI can struggle.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Future Website Might Be Designed for Three Audiences
&lt;/h1&gt;

&lt;p&gt;We used to think about two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Humans
2. Search Engines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think we're adding a third:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3. AI Agents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And AI agents are different from search engines.&lt;/p&gt;

&lt;p&gt;A search engine wants to discover and rank documents.&lt;/p&gt;

&lt;p&gt;An AI agent wants to &lt;strong&gt;understand information and use it to answer questions or perform actions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's a much bigger responsibility for the web.&lt;/p&gt;




&lt;h1&gt;
  
  
  So, Should You Start Optimizing for AI?
&lt;/h1&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;But don't start by stuffing your website with AI-generated text.&lt;/p&gt;

&lt;p&gt;Don't add meaningless schema everywhere.&lt;/p&gt;

&lt;p&gt;Don't create thousands of pages just because an LLM can generate them.&lt;/p&gt;

&lt;p&gt;Instead, start with something much simpler:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Make your website understandable.
&lt;/h3&gt;

&lt;p&gt;If you can't explain what a page is in one sentence, an AI probably won't have an easy time either.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Make important entities explicit.
&lt;/h3&gt;

&lt;p&gt;Companies, products, people, organizations and concepts should have consistent representations.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Build a strong information architecture.
&lt;/h3&gt;

&lt;p&gt;Your website shouldn't feel like a pile of URLs.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Use structured data correctly.
&lt;/h3&gt;

&lt;p&gt;Schema should describe reality, not just exist for the sake of SEO.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Make relationships obvious.
&lt;/h3&gt;

&lt;p&gt;Internal links aren't only navigation.&lt;/p&gt;

&lt;p&gt;They're signals about how information is connected.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Measure it.
&lt;/h3&gt;

&lt;p&gt;This is the part we're still figuring out.&lt;/p&gt;

&lt;p&gt;SEO has decades of tools and metrics.&lt;/p&gt;

&lt;p&gt;AI visibility is still a relatively new space.&lt;/p&gt;

&lt;p&gt;That's what makes it interesting.&lt;/p&gt;




&lt;h1&gt;
  
  
  We're Entering the "Answer Engine" Era
&lt;/h1&gt;

&lt;p&gt;The web was built around documents.&lt;/p&gt;

&lt;p&gt;Search engines organized those documents.&lt;/p&gt;

&lt;p&gt;Social networks organized people and attention.&lt;/p&gt;

&lt;p&gt;Now AI systems are starting to organize &lt;strong&gt;knowledge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And when the interface becomes an AI answer instead of a list of ten blue links, the optimization problem changes.&lt;/p&gt;

&lt;p&gt;The question is no longer only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I rank?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I become the source an AI understands, trusts and chooses to use?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a very different question.&lt;/p&gt;

&lt;p&gt;And I think we're just getting started.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Maybe the next generation of web development won't be about building websites that are optimized for humans &lt;strong&gt;or&lt;/strong&gt; machines.&lt;/p&gt;

&lt;p&gt;It'll be about building websites that are understandable to both.&lt;/p&gt;

&lt;p&gt;Humans need good UX.&lt;/p&gt;

&lt;p&gt;Search engines need crawlable, structured content.&lt;/p&gt;

&lt;p&gt;AI systems need context, relationships and clear signals.&lt;/p&gt;

&lt;p&gt;The best websites will increasingly provide all three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't stop building for Google.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just don't build &lt;em&gt;only&lt;/em&gt; for Google anymore.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>writing</category>
    </item>
    <item>
      <title>Burger Atelier — The Burger, Reimagined 🍔</title>
      <dc:creator>Hamed Farazi</dc:creator>
      <pubDate>Tue, 11 Aug 2026 12:21:21 +0000</pubDate>
      <link>https://dev.to/hamedfarazi/burger-atelier-the-burger-reimagined-3fb7</link>
      <guid>https://dev.to/hamedfarazi/burger-atelier-the-burger-reimagined-3fb7</guid>
      <description>&lt;p&gt;This is a submission for Frontend Challenge - Comfort Food Edition, Perfect Landing&lt;/p&gt;

&lt;p&gt;What I Built&lt;/p&gt;

&lt;p&gt;For this challenge, I wanted to take a familiar comfort food and give it a more refined digital experience.&lt;/p&gt;

&lt;p&gt;I built Burger Atelier, a modern landing page centered around the idea of burgers as more than just fast food — they can be crafted, customized, and presented like an experience.&lt;/p&gt;

&lt;p&gt;The goal was to create a landing page that feels polished while keeping the experience simple and intuitive.&lt;/p&gt;

&lt;p&gt;Highlights&lt;br&gt;
🍔 A bold food-focused visual design&lt;br&gt;
📱 Responsive layout across screen sizes&lt;br&gt;
✨ Interactive UI elements and micro-interactions&lt;br&gt;
🎯 Clear calls-to-action and visual hierarchy&lt;br&gt;
♿ Accessibility-conscious structure and interactions&lt;br&gt;
⚡ Modern React-based frontend&lt;br&gt;
🧩 Reusable components and clean project structure&lt;br&gt;
Demo&lt;/p&gt;

&lt;p&gt;🌐 Live Demo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://frontend-challenge-comfort-food-edi-alpha.vercel.app/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;frontend-challenge-comfort-food-edi-alpha.vercel.app&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;💻 Source Code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/HamedFarazi" rel="noopener noreferrer"&gt;
        HamedFarazi
      &lt;/a&gt; / &lt;a href="https://github.com/HamedFarazi/Frontend-Challenge---Comfort-Food-Edition-Perfect-Landing" rel="noopener noreferrer"&gt;
        Frontend-Challenge---Comfort-Food-Edition-Perfect-Landing
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Perfect Landing: Comfort Food
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Burger Atelier&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Premium interactive burger studio for the DEV Community Frontend Challenge — Comfort Food Edition.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install
npm run dev
npm run build&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;React · TypeScript · Vite · Tailwind CSS v4 · CSS burger craft&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/HamedFarazi/Frontend-Challenge---Comfort-Food-Edition-Perfect-Landing" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Journey&lt;/p&gt;

&lt;p&gt;I approached the project by first thinking about how a food brand could communicate its personality through its landing page.&lt;/p&gt;

&lt;p&gt;Instead of treating the page as a collection of sections, I focused on creating a visual flow that guides the user from the hero section toward the menu and the main call-to-action.&lt;/p&gt;

&lt;p&gt;One of the things I enjoyed most was balancing the visual side of the design with practical frontend considerations. A food landing page can easily become visually heavy, so I tried to keep the interactions clear and the content easy to scan.&lt;/p&gt;

&lt;p&gt;Building the project with React also gave me the opportunity to structure the UI into reusable pieces rather than treating the page as one large component.&lt;/p&gt;

&lt;p&gt;What I learned&lt;/p&gt;

&lt;p&gt;This challenge was a good reminder that a polished landing page isn't only about making things look good.&lt;/p&gt;

&lt;p&gt;The small details matter:&lt;/p&gt;

&lt;p&gt;How quickly users understand what the product is&lt;br&gt;
Whether the primary action is obvious&lt;br&gt;
How the layout behaves on smaller screens&lt;br&gt;
Whether interactive elements feel intentional&lt;br&gt;
How accessible the experience remains beneath the visual layer&lt;/p&gt;

&lt;p&gt;I'm particularly happy with how the final design combines a playful food theme with a more premium visual direction.&lt;/p&gt;

&lt;p&gt;What's Next?&lt;/p&gt;

&lt;p&gt;I'd like to continue experimenting with the concept by adding more interactive ordering flows, richer menu interactions, and potentially turning Burger Atelier into a small multi-page restaurant experience.&lt;/p&gt;

&lt;p&gt;Thanks to the Frontend Challenge team for putting together this challenge! 🙌&lt;/p&gt;

&lt;p&gt;Built with React and modern frontend tools.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🍔 The Perfect Burger — CSS Art</title>
      <dc:creator>Hamed Farazi</dc:creator>
      <pubDate>Tue, 11 Aug 2026 11:25:10 +0000</pubDate>
      <link>https://dev.to/hamedfarazi/the-perfect-burger-css-art-221</link>
      <guid>https://dev.to/hamedfarazi/the-perfect-burger-css-art-221</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, CSS Art&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🍔 The Perfect Burger
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Inspiration
&lt;/h3&gt;

&lt;p&gt;For this challenge, I wanted to create something familiar, fun, and comforting: a classic burger.&lt;/p&gt;

&lt;p&gt;Instead of using images or pre-made assets, I decided to build the entire burger from scratch using HTML and CSS.&lt;/p&gt;

&lt;p&gt;Every ingredient is created with CSS, including the buns, lettuce, tomato, onion, melted cheese, and grilled beef patty.&lt;/p&gt;

&lt;p&gt;The goal was to make something that looks like a burger at first glance, but becomes more interesting when you discover that the entire artwork is built with HTML and CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/Hamed-Farazi/embed/pvNXXqQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://frontend-challenge-comfort-food-edi.vercel.app/" rel="noopener noreferrer"&gt;https://frontend-challenge-comfort-food-edi.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;Source Code:&lt;/strong&gt; &lt;a href="https://github.com/HamedFarazi/Frontend-Challenge---Comfort-Food-Edition" rel="noopener noreferrer"&gt;https://github.com/HamedFarazi/Frontend-Challenge---Comfort-Food-Edition&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also interact with the burger to reveal and separate its individual ingredients.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🍞 CSS-crafted toasted buns&lt;/li&gt;
&lt;li&gt;🥩 Grilled beef patty with CSS texture&lt;/li&gt;
&lt;li&gt;🧀 Melted cheese with organic drips&lt;/li&gt;
&lt;li&gt;🥬 Layered lettuce&lt;/li&gt;
&lt;li&gt;🍅 CSS tomato slices&lt;/li&gt;
&lt;li&gt;🧅 CSS onion rings&lt;/li&gt;
&lt;li&gt;✨ CSS gradients and shadows&lt;/li&gt;
&lt;li&gt;🎬 Interactive ingredient reveal mode&lt;/li&gt;
&lt;li&gt;🖱️ Hover and 3D interactions&lt;/li&gt;
&lt;li&gt;♿ Keyboard accessible interactions&lt;/li&gt;
&lt;li&gt;📱 Responsive design&lt;/li&gt;
&lt;li&gt;🌓 &lt;code&gt;prefers-reduced-motion&lt;/code&gt; support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛠️ Built With
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;HTML5&lt;/li&gt;
&lt;li&gt;CSS3&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎨 CSS Techniques
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Gradients&lt;/li&gt;
&lt;li&gt;Pseudo-elements&lt;/li&gt;
&lt;li&gt;Clip-path&lt;/li&gt;
&lt;li&gt;CSS transforms&lt;/li&gt;
&lt;li&gt;CSS animations&lt;/li&gt;
&lt;li&gt;Box shadows&lt;/li&gt;
&lt;li&gt;CSS variables&lt;/li&gt;
&lt;li&gt;3D perspective&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;I started this project with a simple idea: create a burger using only HTML and CSS.&lt;/p&gt;

&lt;p&gt;The first version was intentionally simple, but as I worked on it, I focused on making the ingredients feel more organic, dimensional, and visually appetizing.&lt;/p&gt;

&lt;p&gt;The most challenging parts were creating the melted cheese, the grilled texture of the beef patty, and making all of the ingredients feel like they were physically stacked together.&lt;/p&gt;

&lt;p&gt;I used CSS gradients, pseudo-elements, clip-path, transforms, shadows, and animations to create the different ingredients and their textures.&lt;/p&gt;

&lt;p&gt;One of my favorite parts is the interactive reveal mode. Instead of keeping the burger as a static illustration, users can separate the ingredients and see each layer individually.&lt;/p&gt;

&lt;p&gt;This also became a way to demonstrate that the burger is actually built from individual HTML/CSS elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'd like to continue experimenting with CSS-based food illustrations and push the interaction and visual details even further.&lt;/p&gt;

&lt;p&gt;Thanks for checking out my submission! 🍔&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
    </item>
    <item>
      <title>Why (and When) You Should Actually Use useMemo and useCallback</title>
      <dc:creator>Hamed Farazi</dc:creator>
      <pubDate>Tue, 11 Aug 2026 08:29:43 +0000</pubDate>
      <link>https://dev.to/hamedfarazi/why-and-when-you-should-actually-use-usememo-and-usecallback-4ij0</link>
      <guid>https://dev.to/hamedfarazi/why-and-when-you-should-actually-use-usememo-and-usecallback-4ij0</guid>
      <description>&lt;p&gt;If you've spent any time in a React codebase, you've probably seen &lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt; sprinkled everywhere — sometimes on every single function and computed value, "just in case." The irony is that this habit often makes an app &lt;em&gt;slower&lt;/em&gt;, not faster, while also making the code harder to read.&lt;/p&gt;

&lt;p&gt;This article is about building an honest mental model for these two hooks: what they actually do under the hood, when they genuinely help, and when they're just noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What these hooks actually do
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt; don't make anything "faster" by default. They &lt;strong&gt;cache a value between renders&lt;/strong&gt; and only recompute it when its dependencies change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;computeExpensiveValue&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="p"&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="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;doSomething&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="p"&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="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useCallback(fn, deps)&lt;/code&gt; is really just &lt;code&gt;useMemo(() =&amp;gt; fn, deps)&lt;/code&gt;. Same mechanism, different return type (a function reference instead of a value).&lt;/p&gt;

&lt;p&gt;Two things matter here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The caching itself has a cost.&lt;/strong&gt; React has to store the previous dependency array, compare it on every render, and hold onto the cached value in memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You only benefit if something downstream actually cares about referential stability or expensive recomputation.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If neither of those is true, you're paying the comparison cost for zero benefit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The case where it's obviously worth it: expensive computations
&lt;/h2&gt;

&lt;p&gt;This one is uncontroversial. If you have a genuinely heavy calculation — sorting a large array, filtering thousands of rows, running some numeric computation — recomputing it on every render (including renders triggered by unrelated state) is wasteful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductList&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filters&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;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;matchesFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filters&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&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;Without &lt;code&gt;useMemo&lt;/code&gt;, every re-render of &lt;code&gt;ProductList&lt;/code&gt; — even one triggered by, say, a tooltip's hover state — would re-run the filter over the whole array. With it, the filter only reruns when &lt;code&gt;products&lt;/code&gt; or &lt;code&gt;filters&lt;/code&gt; actually change.&lt;/p&gt;

&lt;p&gt;The key word is &lt;em&gt;expensive&lt;/em&gt;. Filtering 20 items is not expensive. Filtering 20,000 items, or running a computation with real algorithmic weight, is. Don't reach for &lt;code&gt;useMemo&lt;/code&gt; reflexively — profile first, or at least reason about the actual cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  The case that trips people up: referential stability
&lt;/h2&gt;

&lt;p&gt;This is where most of the confusion lives. &lt;code&gt;useCallback&lt;/code&gt; and &lt;code&gt;useMemo&lt;/code&gt; matter for referential stability in two situations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. When a value or function is a dependency of another hook.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SearchBox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onSearch&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;debouncedSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// if onSearch changes identity every render, this debounce is recreated every time&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;onSearch&lt;/code&gt; isn't stable, &lt;code&gt;useEffect&lt;/code&gt;, &lt;code&gt;useMemo&lt;/code&gt;, or a custom hook relying on it as a dependency will re-run constantly, potentially defeating the entire purpose of the debounce, subscription, or cache it's feeding into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. When a value is passed to a memoized child component.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ExpensiveChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memo&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;ExpensiveChild&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// heavy render logic&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;Parent&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;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Without useCallback, this is a NEW function every render,&lt;/span&gt;
  &lt;span class="c1"&gt;// which defeats React.memo on ExpensiveChild entirely.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;setCount&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;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExpensiveChild&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;Here, &lt;code&gt;React.memo&lt;/code&gt; on &lt;code&gt;ExpensiveChild&lt;/code&gt; only pays off if the props it receives are stable. If &lt;code&gt;handleClick&lt;/code&gt; is a new reference on every render, &lt;code&gt;React.memo&lt;/code&gt; does nothing — the child re-renders anyway, and you've added the overhead of &lt;code&gt;React.memo&lt;/code&gt;'s comparison for no benefit.&lt;/p&gt;

&lt;p&gt;This is the important part: &lt;strong&gt;&lt;code&gt;useCallback&lt;/code&gt; without &lt;code&gt;React.memo&lt;/code&gt; (or a dependency array somewhere downstream) usually does nothing useful.&lt;/strong&gt; The two are a pair. If nothing consumes the stable reference, you're just adding bookkeeping.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it's &lt;em&gt;not&lt;/em&gt; worth it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple values or inline functions passed to plain DOM elements.&lt;/strong&gt; &lt;code&gt;&amp;lt;button onClick={() =&amp;gt; setOpen(true)}&amp;gt;&lt;/code&gt; does not need &lt;code&gt;useCallback&lt;/code&gt;. DOM elements aren't memoized components; a new function reference every render costs essentially nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheap computations.&lt;/strong&gt; &lt;code&gt;const total = a + b&lt;/code&gt; doesn't need &lt;code&gt;useMemo&lt;/code&gt;. The comparison overhead of the hook can genuinely exceed the cost of just recalculating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Just in case" usage without a memoized consumer.&lt;/strong&gt; If nothing downstream is &lt;code&gt;React.memo&lt;/code&gt;-wrapped and nothing uses the value as a dependency, the memoization has no one to benefit from it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Values that change on every render anyway.&lt;/strong&gt; If a dependency changes every render, &lt;code&gt;useMemo&lt;/code&gt; will recompute every time too — you get the comparison cost &lt;em&gt;and&lt;/em&gt; the recomputation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A practical mental checklist
&lt;/h2&gt;

&lt;p&gt;Before adding &lt;code&gt;useMemo&lt;/code&gt; or &lt;code&gt;useCallback&lt;/code&gt;, ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is this computation actually expensive&lt;/strong&gt;, or does it just look intimidating? (Measure if unsure.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does something downstream depend on referential equality&lt;/strong&gt; — a &lt;code&gt;React.memo&lt;/code&gt; child, a &lt;code&gt;useEffect&lt;/code&gt; dependency, a custom hook's cache key?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do the dependencies change rarely&lt;/strong&gt; relative to how often the component re-renders? If they change every time anyway, memoization buys nothing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the answer to #1 or #2 is no, you probably don't need the hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on the React Compiler
&lt;/h2&gt;

&lt;p&gt;It's worth mentioning: the React team has been building an automatic compiler (React Compiler, formerly "React Forget") specifically to memoize things like this automatically, precisely because manual &lt;code&gt;useMemo&lt;/code&gt;/&lt;code&gt;useCallback&lt;/code&gt; placement is easy to get wrong and tedious to maintain. That's a strong signal about how the React team itself views manual memoization — as a mechanism you should reach for deliberately, not a reflex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt; are not performance switches you flip on by default. They're targeted tools for two specific problems: avoiding expensive recomputation, and preserving referential stability for something that actually checks for it. Used with intent, they meaningfully help. Used everywhere, they add cognitive overhead and runtime cost while often fixing nothing at all.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Where to get cover images for a post like this: for genuinely free-to-use, no-attribution-needed photos, &lt;a href="https://unsplash.com" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt; and &lt;a href="https://pexels.com" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt; are the standard choices for dev.to-style covers — search something like "code" or "abstract programming." For a more on-topic illustration (e.g. a diagram-style cover), you can also generate a simple custom SVG/graphic yourself — dev.to covers are just images, so anything royalty-free works.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I Used BFS to Find the Shortest Route in Tehran Metro with React</title>
      <dc:creator>Hamed Farazi</dc:creator>
      <pubDate>Mon, 10 Aug 2026 22:48:07 +0000</pubDate>
      <link>https://dev.to/hamedfarazi/how-i-used-bfs-to-find-the-shortest-route-in-tehran-metro-with-react-3p8o</link>
      <guid>https://dev.to/hamedfarazi/how-i-used-bfs-to-find-the-shortest-route-in-tehran-metro-with-react-3p8o</guid>
      <description>&lt;p&gt;When I started building Metrino, a Tehran Metro route planner, I had a simple question:&lt;/p&gt;

&lt;p&gt;«How can I find the shortest route between two metro stations?»&lt;/p&gt;

&lt;p&gt;The UI was only one part of the problem. Underneath the map and station selectors, I needed a way to represent the Tehran Metro network and calculate a route between two stations.&lt;/p&gt;

&lt;p&gt;The solution was to model the metro network as a graph and use Breadth-First Search (BFS) to find the shortest path.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpblp19cwql7hhc8nsbqx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpblp19cwql7hhc8nsbqx.png" alt=" " width="800" height="723"&gt;&lt;/a&gt;&lt;br&gt;
🚇 Turning the Metro Into a Graph&lt;/p&gt;

&lt;p&gt;Instead of thinking about the metro as a visual map, I represented it as a graph.&lt;/p&gt;

&lt;p&gt;Each station is a node.&lt;/p&gt;

&lt;p&gt;Each connection between two stations is an edge.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxwahqqzng3c367j437nt.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxwahqqzng3c367j437nt.png" alt=" " width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Station A&lt;br&gt;
    │&lt;br&gt;
    ▼&lt;br&gt;
Station B ─── Station C&lt;br&gt;
                 │&lt;br&gt;
                 ▼&lt;br&gt;
              Station D&lt;/p&gt;

&lt;p&gt;In my data model, each station contains a list of connected stations.&lt;/p&gt;

&lt;p&gt;This means I can ask:&lt;/p&gt;

&lt;p&gt;«"Which stations can I reach directly from this station?"»&lt;/p&gt;

&lt;p&gt;The application builds a bidirectional adjacency map when the metro data is initialized:&lt;/p&gt;

&lt;p&gt;const adj = new Map&amp;gt;();&lt;/p&gt;

&lt;p&gt;this._stations.forEach((s) =&amp;gt; {&lt;br&gt;
  if (!adj.has(s.id)) adj.set(s.id, new Set());&lt;/p&gt;

&lt;p&gt;s.connectedStationIds.forEach((nid) =&amp;gt; {&lt;br&gt;
    adj.get(s.id)!.add(nid);&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Add reverse direction
if (!adj.has(nid)) adj.set(nid, new Set());
adj.get(nid)!.add(s.id);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;});&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;This gives the route finder a structure that is easy to traverse.&lt;/p&gt;

&lt;p&gt;🧭 Why BFS?&lt;/p&gt;

&lt;p&gt;For the first version of the route planner, I wanted to minimize the number of station-to-station hops.&lt;/p&gt;

&lt;p&gt;That's where BFS works well.&lt;/p&gt;

&lt;p&gt;Breadth-First Search explores a graph level by level:&lt;/p&gt;

&lt;p&gt;Start&lt;br&gt;
 │&lt;br&gt;
 ├── A&lt;br&gt;
 ├── B&lt;br&gt;
 └── C&lt;br&gt;
      │&lt;br&gt;
      ├── D&lt;br&gt;
      ├── E&lt;br&gt;
      └── F&lt;/p&gt;

&lt;p&gt;It first explores the stations closest to the starting station, then moves to the next level.&lt;/p&gt;

&lt;p&gt;Because every connection has the same cost in this version of the problem, the first route BFS finds to the destination is the route with the fewest hops.&lt;/p&gt;

&lt;p&gt;💻 The Actual BFS Implementation&lt;/p&gt;

&lt;p&gt;Here is the actual "findPath" implementation from Metrino:&lt;/p&gt;

&lt;p&gt;findPath(fromId: string, toId: string): string[] | null {&lt;br&gt;
  if (fromId === toId) return [fromId];&lt;/p&gt;

&lt;p&gt;const visited = new Set();&lt;/p&gt;

&lt;p&gt;const queue: Array&amp;lt;{ id: string; path: string[] }&amp;gt; = [&lt;br&gt;
    { id: fromId, path: [fromId] },&lt;br&gt;
  ];&lt;/p&gt;

&lt;p&gt;visited.add(fromId);&lt;/p&gt;

&lt;p&gt;while (queue.length &amp;gt; 0) {&lt;br&gt;
    const { id, path } = queue.shift()!;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const neighbors = this._adjacency.get(id) ?? [];

for (const neighbor of neighbors) {
  if (neighbor === toId) {
    return [...path, neighbor];
  }

  if (!visited.has(neighbor)) {
    visited.add(neighbor);

    queue.push({
      id: neighbor,
      path: [...path, neighbor],
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;return null;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Let's break it down.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Handle the same station&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;if (fromId === toId) return [fromId];&lt;/p&gt;

&lt;p&gt;If the origin and destination are the same station, there is nothing to calculate.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep track of visited stations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const visited = new Set();&lt;/p&gt;

&lt;p&gt;Without this, the algorithm could keep visiting the same stations again and again.&lt;/p&gt;

&lt;p&gt;I immediately mark the starting station as visited:&lt;/p&gt;

&lt;p&gt;visited.add(fromId);&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the queue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const queue: Array&amp;lt;{ id: string; path: string[] }&amp;gt; = [&lt;br&gt;
  { id: fromId, path: [fromId] },&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;The queue contains the station we're currently exploring and the path that led to it.&lt;/p&gt;

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

&lt;p&gt;{&lt;br&gt;
  id: "station-b",&lt;br&gt;
  path: ["station-a", "station-b"]&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This makes it easy to return the complete route once we reach the destination.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Explore the graph&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;while (queue.length &amp;gt; 0) {&lt;/p&gt;

&lt;p&gt;As long as there are stations waiting to be explored, BFS continues.&lt;/p&gt;

&lt;p&gt;We remove the first item from the queue:&lt;/p&gt;

&lt;p&gt;const { id, path } = queue.shift()!;&lt;/p&gt;

&lt;p&gt;Then get all directly connected stations:&lt;/p&gt;

&lt;p&gt;const neighbors = this._adjacency.get(id) ?? [];&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the destination&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;if (neighbor === toId) {&lt;br&gt;
  return [...path, neighbor];&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;As soon as BFS reaches the destination, we return the complete station sequence.&lt;/p&gt;

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

&lt;p&gt;Station A&lt;br&gt;
   ↓&lt;br&gt;
Station B&lt;br&gt;
   ↓&lt;br&gt;
Station C&lt;br&gt;
   ↓&lt;br&gt;
Station D&lt;/p&gt;

&lt;p&gt;The returned result would look like:&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;
  "station-a",&lt;br&gt;
  "station-b",&lt;br&gt;
  "station-c",&lt;br&gt;
  "station-d"&lt;br&gt;
]&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add unvisited stations to the queue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;if (!visited.has(neighbor)) {&lt;br&gt;
  visited.add(neighbor);&lt;/p&gt;

&lt;p&gt;queue.push({&lt;br&gt;
    id: neighbor,&lt;br&gt;
    path: [...path, neighbor],&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This is what allows BFS to move through the network level by level.&lt;/p&gt;

&lt;p&gt;🔄 What About Line Transfers?&lt;/p&gt;

&lt;p&gt;Finding the shortest path is only part of the problem.&lt;/p&gt;

&lt;p&gt;Tehran Metro has multiple lines, so after finding the station path, I also need to determine where passengers change lines.&lt;/p&gt;

&lt;p&gt;The route service looks at consecutive stations and finds their shared line:&lt;/p&gt;

&lt;p&gt;private _getCommonLine(a: Station, b: Station): number | null {&lt;br&gt;
  for (const line of a.lines) {&lt;br&gt;
    if (b.lines.includes(line)) return line;&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;return null;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Then the route builder can detect when the route changes from one line to another.&lt;/p&gt;

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

&lt;p&gt;Line 1&lt;br&gt;
  │&lt;br&gt;
  │&lt;br&gt;
  ▼&lt;br&gt;
Station A&lt;br&gt;
  │&lt;br&gt;
  ▼&lt;br&gt;
Transfer Station&lt;br&gt;
  │&lt;br&gt;
  │&lt;br&gt;
  ▼&lt;br&gt;
Line 4&lt;br&gt;
  │&lt;br&gt;
  ▼&lt;br&gt;
Station B&lt;/p&gt;

&lt;p&gt;The transfer is recorded separately from the BFS path.&lt;/p&gt;

&lt;p&gt;⏱️ Estimating Travel Time&lt;/p&gt;

&lt;p&gt;BFS gives me the route, but it doesn't tell me how long the journey will take.&lt;/p&gt;

&lt;p&gt;So after finding the path, Metrino calculates an estimated travel time using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;approximate distance between stations&lt;/li&gt;
&lt;li&gt;an average train speed&lt;/li&gt;
&lt;li&gt;station dwell time&lt;/li&gt;
&lt;li&gt;transfer time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current implementation uses:&lt;/p&gt;

&lt;p&gt;const TRANSFER_PENALTY_MIN = 3;&lt;br&gt;
const STATION_DWELL_SEC = 30;&lt;br&gt;
const AVG_SPEED_KMH = 40;&lt;/p&gt;

&lt;p&gt;The total estimate is then built from these values.&lt;/p&gt;

&lt;p&gt;These are estimates, not official Tehran Metro schedules.&lt;/p&gt;

&lt;p&gt;🧩 Separating the Algorithm From the UI&lt;/p&gt;

&lt;p&gt;One thing I wanted to avoid was putting route calculation directly inside React components.&lt;/p&gt;

&lt;p&gt;Instead, I separated the responsibilities:&lt;/p&gt;

&lt;p&gt;React UI&lt;br&gt;
   ↓&lt;br&gt;
MetroRouteService&lt;br&gt;
   ↓&lt;br&gt;
MetroDataService&lt;br&gt;
   ↓&lt;br&gt;
Metro Graph / Metro Data&lt;/p&gt;

&lt;p&gt;"MetroDataService" handles the station, line, connection, and adjacency data.&lt;/p&gt;

&lt;p&gt;"MetroRouteService" takes the resulting path and turns it into a complete route containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;origin&lt;/li&gt;
&lt;li&gt;destination&lt;/li&gt;
&lt;li&gt;station sequence&lt;/li&gt;
&lt;li&gt;segments&lt;/li&gt;
&lt;li&gt;transfers&lt;/li&gt;
&lt;li&gt;distance&lt;/li&gt;
&lt;li&gt;estimated time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation makes the UI much easier to work with.&lt;/p&gt;

&lt;p&gt;📈 What's Next?&lt;/p&gt;

&lt;p&gt;BFS was a good starting point because the first version of Metrino only needed to minimize the number of station hops.&lt;/p&gt;

&lt;p&gt;But the real-world "best route" isn't always the route with the fewest stations.&lt;/p&gt;

&lt;p&gt;For example, a slightly longer route might be better if it has fewer transfers or takes less time.&lt;/p&gt;

&lt;p&gt;That's why one of the next improvements I'd like to explore is weighted pathfinding, using algorithms such as Dijkstra's algorithm or A*.&lt;/p&gt;

&lt;p&gt;The goal would be to consider things like:&lt;/p&gt;

&lt;p&gt;Station count&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Travel time&lt;/li&gt;
&lt;li&gt;Transfer penalties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;instead of only counting station hops.&lt;/p&gt;

&lt;p&gt;🚀 What I Learned&lt;/p&gt;

&lt;p&gt;The biggest lesson from building this feature was that many problems that look like UI problems are actually data structure and algorithm problems.&lt;/p&gt;

&lt;p&gt;The metro map is a UI.&lt;/p&gt;

&lt;p&gt;But underneath that UI, it's a graph.&lt;/p&gt;

&lt;p&gt;Once I represented the metro network as a graph, route calculation became much easier to reason about.&lt;/p&gt;

&lt;p&gt;BFS gave me a simple and reliable starting point, and it was enough to power the first version of the route planner.&lt;/p&gt;

&lt;p&gt;🚇 Try Metrino&lt;/p&gt;

&lt;p&gt;I built Metrino as a Persian-first Tehran Metro web application with React, TypeScript and Vite.&lt;/p&gt;

&lt;p&gt;Live Demo: &lt;a href="https://metrino.vercel.app/" rel="noopener noreferrer"&gt;https://metrino.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/HamedFarazi/metroapp" rel="noopener noreferrer"&gt;https://github.com/HamedFarazi/metroapp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is open source, and I'm continuing to improve the routing, maps, PWA experience, and accessibility.&lt;/p&gt;

&lt;p&gt;Thanks for reading 💫&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
