<?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: Fabian Frank Werner</title>
    <description>The latest articles on Forem by Fabian Frank Werner (@fabianfrankwerner).</description>
    <link>https://forem.com/fabianfrankwerner</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%2F2664526%2Fd7b28645-176d-4e7d-968a-7dbba17c6bf7.png</url>
      <title>Forem: Fabian Frank Werner</title>
      <link>https://forem.com/fabianfrankwerner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fabianfrankwerner"/>
    <language>en</language>
    <item>
      <title>Uh oh... Cloudflare just turned evil</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Sun, 22 Mar 2026 23:00:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/uh-oh-cloudflare-just-turned-evil-42pc</link>
      <guid>https://forem.com/fabianfrankwerner/uh-oh-cloudflare-just-turned-evil-42pc</guid>
      <description>&lt;p&gt;So, the headline today is that Cloudflare—the company famous for being the absolute, undisputed bouncer of the internet—just released a brand new tool for developers. And the tool... is a bot. A web scraper. The big, hilarious irony here is that the company that has spent the last decade building the most sophisticated anti-bot protection on the planet, just handed developers a master key to scrape the web for AI.&lt;/p&gt;

&lt;p&gt;Now, if you’re a developer, or you’re building AI apps, or you run a startup, this is a massive deal. It is fundamentally shifting the landscape of how data gets fed to large language models. But if you missed the announcement, or the absolute chaotic reaction to it on Twitter over the last few days, we really need to dive into this. Because on one hand, this new tool is an absolute masterclass in infrastructure engineering. But on the other hand, it has put Cloudflare in this incredibly comical, paradoxical position in the market. And it is actively sending shockwaves through a whole ecosystem of startups—like Firecrawl—who specialized in doing exactly what Cloudflare just made essentially native.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Anti-bot. Pro-web.
&lt;/h2&gt;

&lt;p&gt;Let’s rewind and set the stage. If you use the internet—which, assuming you are watching this video, you do—you know Cloudflare. They handle the traffic for roughly 20 percent of the entire web. When you go to a website and get that little 'Checking if you are human' box, or you get blocked because your IP looks suspicious, that’s Cloudflare.&lt;/p&gt;

&lt;p&gt;Over the last couple of years, as the AI boom took off, we entered this era of aggressive web scraping. Companies building LLMs, RAG pipelines, and AI agents were just relentlessly hammering websites to extract training data. It was a complete free-for-all. Website owners were furious because their server bills were skyrocketing while getting zero real human traffic. So, Cloudflare stepped in as the hero. They rolled out one-click toggles to block AI bots. They introduced 'AI Crawl Control.' They established themselves as the credible, responsible adult in the room, protecting the open web from the AI data vampires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Hello, Crawler!
&lt;/h2&gt;

&lt;p&gt;Which brings us to March 10th, 2026. Just a few days ago, the Cloudflare Developers account on X posted the announcement for a brand new open beta feature in their Browser Rendering suite: The &lt;code&gt;crawl&lt;/code&gt; endpoint. The pitch is simple: One API call, and an entire site is crawled. No scripts to write. No headless browsers to manage. Just pure content returned in HTML, Markdown, or structured JSON. Let that sink in. The company that stops bots just released a managed, scalable bot as a service.&lt;/p&gt;

&lt;p&gt;If you look at their official REST API docs, this endpoint is basically magic for developers building AI tools. You send a single POST request to the API with a starting URL. That’s literally it.&lt;/p&gt;

&lt;p&gt;Cloudflare’s infrastructure automatically discovers the pages, spins up a headless browser to render the JavaScript, extracts the content using Workers AI, and hands you back a job ID. Because it runs asynchronously, you just check back with a GET request, and boom—perfectly structured data ready for your RAG pipeline. You don't have to set up Puppeteer. You don't have to hunt for complex CSS selectors. And Cloudflare even baked in features like &lt;code&gt;modifiedSince&lt;/code&gt; and &lt;code&gt;maxAge&lt;/code&gt; to do incremental crawling, saving you compute time.&lt;/p&gt;

&lt;p&gt;But this is where the market positioning becomes so incredibly funny, and why the internet is making so many memes about it. Cloudflare is essentially saying, 'We protect websites from scrapers... unless it’s &lt;em&gt;our&lt;/em&gt; scraper.' But, to be completely fair to Cloudflare, they are playing by the rules. If you dive into their &lt;code&gt;robots.txt&lt;/code&gt;documentation for this new endpoint, they are very explicit: This is a &lt;em&gt;polite&lt;/em&gt; bot.&lt;/p&gt;

&lt;p&gt;Cloudflare hardcoded the User-Agent to &lt;code&gt;CloudflareBrowserRenderingCrawler/1.0&lt;/code&gt;. You cannot change it. You cannot spoof it. It rigorously respects &lt;code&gt;robots.txt&lt;/code&gt; files, crawl delays, and most importantly, it obeys Cloudflare’s own AI Crawl Control. So, if a site owner flips the switch to block AI bots, Cloudflare’s own bouncer will kick its own bot out of the club. It’s the ultimate 'I am playing both sides so I always come out on top' strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Friendly fire...
&lt;/h2&gt;

&lt;p&gt;But let's talk about the real casualties of this announcement. Because this isn't just about Cloudflare being hypocritical. This is about what happens to the startups that were built in the gap that Cloudflare just closed. Enter Firecrawl.&lt;/p&gt;

&lt;p&gt;If you haven't heard of Firecrawl, they are an absolutely brilliant startup, specifically built for the AI era. Their entire value proposition is turning complex websites into clean Markdown and structured JSON for AI agents. They handle the hard stuff—rotating proxies, bypassing captchas, rendering complex Single Page Applications. They have thousands of stars on GitHub and became the absolute darling of the AI developer community.&lt;/p&gt;

&lt;p&gt;A huge part of Firecrawl’s appeal was that it was really good at getting past... you guessed it... Cloudflare’s anti-bot protections. If you've ever tried to scrape a site and got hit with a Cloudflare 403 Forbidden error, Firecrawl was the tool you paid for to make that headache go away. And now? Cloudflare essentially looked at Firecrawl's entire business model and said, 'Yeah, we'll just build that directly into our edge network.'&lt;/p&gt;

&lt;p&gt;It is the textbook definition of being 'Sherlocked.' Cloudflare’s new &lt;code&gt;crawl&lt;/code&gt; endpoint does exactly what Firecrawl does, but it runs natively on Cloudflare’s infrastructure. And the limits are aggressive. On the free tier, developers get 5 crawl jobs a day with up to 100 pages per crawl. On the paid Workers plan? It’s massive. Developers are already pointing out that integrating this native endpoint is an order of magnitude cheaper and faster than paying for a specialized third-party scraping service. An entire startup's moat just evaporated overnight by an infrastructure giant.&lt;/p&gt;

&lt;p&gt;So what does a company like Firecrawl do in the coming months? Because they are going to have a hard time, and they need to pivot fast. If I'm Firecrawl, I can't compete on basic markdown extraction anymore. Cloudflare won that race. But remember, Cloudflare's crawler is locked into being a polite bot. It respects the rules.&lt;/p&gt;

&lt;p&gt;Firecrawl might have to lean heavily into being the 'unpolite' bot—the one that uses advanced residential proxies and browser fingerprinting to scrape sites that actively &lt;em&gt;want&lt;/em&gt; to block scrapers. Basically, when Cloudflare's polite bot hits a wall, you call Firecrawl. The other pivot is deep, agentic interaction. Cloudflare’s endpoint is passive; it loads the page, runs the AI prompt, and returns data. Firecrawl has to double down on features that let an AI actively click buttons, fill out login forms, and navigate behind authentication walls. They have to become an AI agent workspace, not just a data pipe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4: Gatekeeper. Gateway. Tollbooth.
&lt;/h2&gt;

&lt;p&gt;But zooming out, this whole situation is a fascinating case study in market dynamics and the future of the web. All these headlines and Twitter threads are funny: 'Cloudflare is the bot and the anti-bot.' But it popped this thought into my head that maybe, in the modern AI era, who owns the smartest Large Language Model is not as important as who controls the training data pipeline.&lt;/p&gt;

&lt;p&gt;We have OpenAI, Google, and Anthropic spending billions on compute to train models. But those models are starving for high-quality, real-time web data. Cloudflare sits directly in front of that data. They are the tollbooth. For a year, they used their power to shut the gates and protect the publishers. And now, with the &lt;code&gt;crawl&lt;/code&gt; endpoint, they are opening a sanctioned, highly controlled side-door.&lt;/p&gt;

&lt;p&gt;They aren't just a Content Delivery Network anymore; they are positioning themselves as the primary data broker for the entire artificial intelligence industry. If you want the data cleanly, legally, and cheaply, you go through Cloudflare. It is a brilliant business move. It is hilarious market positioning. And it is absolutely terrifying if you are a startup trying to build middleware on the internet.&lt;/p&gt;

&lt;p&gt;But I am really curious what you all think. For the developers watching this—are you going to drop your current scraping setups and move everything over to Cloudflare's new native &lt;code&gt;crawl&lt;/code&gt; endpoint? Do you think specialized tools like Firecrawl are doomed, or do they still have a place in your stack for handling the really hard, dirty work of scraping behind logins? And honestly, how do you feel about Cloudflare playing both the cop and the getaway driver in the AI data heist?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Elysia in 100 Seconds</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Sun, 01 Feb 2026 23:00:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/elysia-in-100-seconds-2a8k</link>
      <guid>https://forem.com/fabianfrankwerner/elysia-in-100-seconds-2a8k</guid>
      <description>&lt;p&gt;&lt;a href="https://elysiajs.com" rel="noopener noreferrer"&gt;ElysiaJS&lt;/a&gt;. A high-performance, statically typed web framework famous for being faster than your caffeine jitters. It makes you realize how slow Express actually is.&lt;/p&gt;

&lt;p&gt;It’s the framework of choice for developers who want the raw speed of the Bun runtime but still want their code to look like it was written by a human.&lt;/p&gt;

&lt;p&gt;It was created in 2022 by the developer SaltyAom. And it’s widely believed to be named after the character Elysia from Honkai Impact 3rd. Because high-performance software engineering and anime addiction are basically the same Venn diagram at this point.&lt;/p&gt;

&lt;p&gt;Elysia was built to solve a specific problem: Type safety usually comes with a tax—either a runtime performance penalty or extensive boilerplate. Elysia cheats the system by leveraging Bun's Just-In-Time compiler and TypeBox to give you end-to-end type safety with zero code generation. Unlike NestJS, you don't need a PhD in decorators to define a route, and unlike Express, it won't let you ship broken code to production.&lt;/p&gt;

&lt;p&gt;To get started, install Bun, then create a file ending in &lt;code&gt;.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Initialize the app by calling &lt;code&gt;new Elysia()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Define your logic using a chainable, fluent API. Use &lt;code&gt;.get()&lt;/code&gt; to define a route. Notice how there are no complex return types—just return an object, and it automatically serializes it to JSON.&lt;/p&gt;

&lt;p&gt;But the real magic happens when you handle data. Add a schema object to the route using the &lt;code&gt;t&lt;/code&gt; variable. This validates incoming data at runtime &lt;em&gt;and&lt;/em&gt; automatically infers the TypeScript types for your IDE. If you try to access a property that doesn't exist, the compiler yells at you before you even run the code.&lt;/p&gt;

&lt;p&gt;It even features the "Eden Treaty," a system that allows your frontend to import the backend's type definitions directly, giving you autocompletion for your API calls on the client side.&lt;/p&gt;

&lt;p&gt;Now, compile and run your code with &lt;code&gt;bun run&lt;/code&gt;, and watch your server start up in microseconds.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/TOyQmc0n2HA"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Python vs Ruby - I built the same github analyzer with both</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Sun, 18 Jan 2026 23:00:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/python-vs-ruby-i-built-the-same-github-analyzer-with-both-51gj</link>
      <guid>https://forem.com/fabianfrankwerner/python-vs-ruby-i-built-the-same-github-analyzer-with-both-51gj</guid>
      <description>&lt;p&gt;If you are picking a high-level programming language today, there are two competing heavyweights. In the red corner, we have &lt;strong&gt;Ruby&lt;/strong&gt;—the language designed for developer happiness; and in the blue corner, we have &lt;strong&gt;Python&lt;/strong&gt;—the data juggernaut that ate the world.&lt;/p&gt;

&lt;p&gt;Both tools achieve the same fundamental goal: they let you build powerful applications fast. But there is a world of difference in the philosophy. No matter which one you choose, the other one automatically becomes your arch-nemesis. Because you don’t choose anything but the &lt;em&gt;absolute&lt;/em&gt; best technology out there.&lt;/p&gt;

&lt;p&gt;To figure out which one is best, I built the exact same &lt;strong&gt;GitHub Repository Analyzer&lt;/strong&gt; with both Python and Ruby.&lt;/p&gt;

&lt;p&gt;In today’s video, we are going to dive into a detailed side-by-side comparison of the syntax, data processing, web frameworks, performance, and of course, the ecosystem! By the end of this video, you will know exactly which one fits best into your ideology.&lt;/p&gt;

&lt;p&gt;But this isn't a line-by-line coding tutorial. If you want to learn how to build these apps from scratch, then &lt;a href="https://fabianfrankwerner.com/newsletter" rel="noopener noreferrer"&gt;subscribe to my newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The tool I built is a web application that takes a GitHub username and repository name as input. It hits the GitHub API, fetches the commit history, and analyzes the data to tell us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Who is the most active contributor?&lt;/li&gt;
&lt;li&gt;What days of the week are most productive?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the Python version, we are using Flask for the web server and Pandas for the data crunching.&lt;/p&gt;

&lt;p&gt;For the Ruby version, we are using Sinatra (the grandfather of micro-frameworks) and pure Ruby Enumerables to prove a point about the standard library.&lt;/p&gt;

&lt;p&gt;Let's talk about the first thing you do: setting up your environment. This is where the pain begins.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Python&lt;/strong&gt;, we need to isolate our dependencies. If you are a beginner, you Google "how to install packages." You see &lt;code&gt;pip install&lt;/code&gt;. You do it. And suddenly, you have installed a library globally on your system, breaking your OS-level Python.&lt;/p&gt;

&lt;p&gt;So, you learn about Virtual Environments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;python -m venv venv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;source venv/bin/activate&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You create a &lt;code&gt;requirements.txt&lt;/code&gt; file. But wait, &lt;code&gt;requirements.txt&lt;/code&gt; is dumb. It doesn't handle dependency resolution well. So maybe you should use &lt;code&gt;Poetry&lt;/code&gt;? Or &lt;code&gt;Pipenv&lt;/code&gt;? Or &lt;code&gt;Conda&lt;/code&gt;? Or &lt;code&gt;Rye&lt;/code&gt;? The Python ecosystem is currently in a civil war regarding packaging. It works, but it feels like assembling IKEA furniture without the instructions.&lt;/p&gt;

&lt;p&gt;Now, let's look at Ruby.&lt;/p&gt;

&lt;p&gt;Ruby has had this solved for over a decade. It’s called Bundler.&lt;/p&gt;

&lt;p&gt;You create a file called &lt;code&gt;Gemfile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s1"&gt;'https://rubygems.org'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'sinatra'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'httparty'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You run &lt;code&gt;bundle install&lt;/code&gt;. That's it. Bundler manages your dependencies, locks the versions in a &lt;code&gt;Gemfile.lock&lt;/code&gt;, and ensures that if I send this code to you, it works exactly the same way. It is the gold standard that every other language, including JavaScript's npm and Rust's Cargo, tried to copy.&lt;/p&gt;

&lt;p&gt;Now we write the code. This is where the religious war happens.&lt;/p&gt;

&lt;p&gt;Python is famous for &lt;strong&gt;significant whitespace&lt;/strong&gt;. It uses indentation to define blocks of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_repo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Analyzing &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No user provided.&lt;/span&gt;&lt;span class="sh"&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 I mess up the indentation by one space—just one space—the program crashes. &lt;code&gt;IndentationError&lt;/code&gt;. This forces you to write clean code. You physically cannot write messy, unindented Python. It enforces structure. It is visual discipline.&lt;/p&gt;

&lt;p&gt;Now, look at &lt;strong&gt;Ruby&lt;/strong&gt;. Ruby doesn't care about your whitespace. It cares about keywords. Specifically, the word &lt;code&gt;end&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_repo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Analyzing &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"No user provided."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See that? No colons. No required indentation (though you &lt;em&gt;should&lt;/em&gt; indent). But you have to close everything with &lt;code&gt;end&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But the difference goes deeper. Python believes in "Explicit is better than implicit."&lt;/p&gt;

&lt;p&gt;If you want to access a variable, you name it. If you want to return a value, you type return.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby believes in Magic.&lt;/p&gt;

&lt;p&gt;In Ruby, the last line of a function is automatically returned. You don't need to type return. Parentheses are optional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
  &lt;span class="s2"&gt;"Hello, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python looks like a blueprint. Ruby looks like a sentence.&lt;/p&gt;

&lt;p&gt;Let's hit the GitHub API.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Python&lt;/strong&gt;, we use the library &lt;code&gt;requests&lt;/code&gt;. It is arguably the most famous Python library ever written. Its tagline is "HTTP for Humans."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_commits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.github.com/repos/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/commits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is beautiful. It is simple. It does exactly what you expect.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Ruby&lt;/strong&gt;, we use &lt;code&gt;HTTParty&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'httparty'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GithubFetcher&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;HTTParty&lt;/span&gt;
  &lt;span class="n"&gt;base_uri&lt;/span&gt; &lt;span class="s1"&gt;'https://api.github.com'&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_commits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/repos/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/commits"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the difference? In Python, &lt;code&gt;requests&lt;/code&gt; is a tool we &lt;em&gt;use&lt;/em&gt;. In Ruby, &lt;code&gt;HTTParty&lt;/code&gt; is a module we &lt;em&gt;include&lt;/em&gt;. We are literally extending our class with HTTP capabilities. This is the &lt;strong&gt;Object-Oriented&lt;/strong&gt; nature of Ruby shining through. In Ruby, everything is an object, and classes are open for modification. We aren't just calling a function; we are building an object that &lt;em&gt;knows&lt;/em&gt; how to talk to GitHub.&lt;/p&gt;

&lt;p&gt;Now we have a list of raw commits. We need to count them by author and find the busiest days.&lt;/p&gt;

&lt;p&gt;Python is the king of data because of libraries like Pandas. We don't write loops in Python if we can avoid it. We use DataFrames.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_commits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Load data into a DataFrame
&lt;/span&gt;    &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Normalize the nested JSON to get author names
&lt;/span&gt;    &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;author_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;commit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;author&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;# Count commits by author
&lt;/span&gt;    &lt;span class="n"&gt;top_authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;author_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;value_counts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;top_authors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is incredibly powerful. With three lines of code, I can process millions of rows. &lt;strong&gt;However&lt;/strong&gt;, it is heavy. I had to install a massive library just to count some items. It feels like bringing a tank to a knife fight. It’s effective, but it’s overkill.&lt;/p&gt;

&lt;p&gt;Ruby doesn't need Pandas for this. Ruby's standard library has a module called &lt;code&gt;Enumerable&lt;/code&gt;, which is legendary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_commits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;commits&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'commit'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'author'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Extract names&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tally&lt;/span&gt;                                     &lt;span class="c1"&gt;# Count occurrences&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_by&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;             &lt;span class="c1"&gt;# Sort descending&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                   &lt;span class="c1"&gt;# Get top 5&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;                                      &lt;span class="c1"&gt;# Convert back to Hash&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at that Ruby code. &lt;code&gt;.map&lt;/code&gt;, &lt;code&gt;.tally&lt;/code&gt;, &lt;code&gt;.sort_by&lt;/code&gt;, &lt;code&gt;.take&lt;/code&gt;. It reads like English instructions. "Map the names, tally them up, sort by count, take the top 5."&lt;/p&gt;

&lt;p&gt;I didn't need to install any external libraries. This is built into the language. This is what Ruby fans mean when they say the language makes them happy. You don't search for a library; you just ask the data to transform itself.&lt;/p&gt;

&lt;p&gt;Now we need to put this on the web. We are using "Micro-frameworks." These are designed to let you spin up a server in a single file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/analyze/&amp;lt;user&amp;gt;/&amp;lt;repo&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_commits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flask uses Decorators. This is a very "Pythonic" concept. We are wrapping our function with metadata that tells the server "When this URL is hit, run this function." It is explicit. You can see exactly what is happening.&lt;/p&gt;

&lt;p&gt;Sinatra actually inspired Flask. Let's see how the original does it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sinatra'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'json'&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/analyze/:user/:repo'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_commits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:repo&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sinatra uses a &lt;strong&gt;DSL&lt;/strong&gt; meaning Domain Specific Language. Notice we aren't defining a class or a function in the traditional sense. We are just typing &lt;code&gt;get&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is Ruby's superpower: Metaprogramming. Ruby allows library authors to change the syntax of the language to fit the domain. In Sinatra, it feels like you aren't writing Ruby; you are writing "HTTP".&lt;/p&gt;

&lt;p&gt;However, this magic comes at a cost. If you don't know Sinatra, you might look at this and ask, "Where is the &lt;code&gt;get&lt;/code&gt; method defined? Is it a global variable? A method on an object?" It’s implicit. Python's decorators make it obvious that &lt;code&gt;analyze&lt;/code&gt; is just a function.&lt;/p&gt;

&lt;p&gt;We have built the app in both. But an app doesn't exist in a vacuum. It exists in an ecosystem.&lt;/p&gt;

&lt;p&gt;If you choose Python, you have superpowers. You have NumPy for math. PyTorch and TensorFlow for AI. Django for web. Ansible for automation. BeautifulSoup for scraping.&lt;/p&gt;

&lt;p&gt;Python is the second-best language for &lt;em&gt;everything&lt;/em&gt;. It might not be the fastest, it might not be the prettiest, but if you need to integrate a web app with a Machine Learning model, Python is the only logical choice. The job market is massive. You can work in Web, Data, Ops, Security, or Academic Research.&lt;/p&gt;

&lt;p&gt;If you choose Ruby, you really only have one superpower: Web Development.&lt;/p&gt;

&lt;p&gt;Ruby is almost synonymous with Ruby on Rails. Rails is a masterpiece. It allows small teams to build massive products like GitHub, Shopify, and Twitch, incredibly fast.&lt;/p&gt;

&lt;p&gt;But outside of web dev? Ruby is... quiet. You &lt;em&gt;can&lt;/em&gt; use it for scripting, but nobody uses it for AI. Nobody uses it for embedded systems. If you learn Ruby today, you are effectively learning to be a Rails Backend Engineer. That is a lucrative career, but it is a narrower path.&lt;/p&gt;

&lt;p&gt;So, is Ruby dead? &lt;strong&gt;Absolutely not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing the Ruby version of this project was &lt;em&gt;fun&lt;/em&gt;. That’s the only word for it. The code flowed from my fingertips. I didn't have to fight the syntax. I didn't have to look up imports. The &lt;code&gt;Enumerable&lt;/code&gt; methods felt like superpowers. If you are a solo developer or a startup founder who wants to build a product fast and enjoy the process, Ruby is still the king of productivity.&lt;/p&gt;

&lt;p&gt;But writing the Python version felt like &lt;strong&gt;Engineering&lt;/strong&gt;. It was robust. It was explicit. And I knew that if I wanted to expand this project—say, to use AI to predict future commit activity—I would already be in the right ecosystem.&lt;/p&gt;

&lt;p&gt;So, choose &lt;strong&gt;Ruby&lt;/strong&gt; if you value the &lt;em&gt;art&lt;/em&gt; of coding. Choose it if you want to build web applications with a framework that respects your time and intelligence. Choose it if you want to fall in love with programming again.&lt;/p&gt;

&lt;p&gt;But, choose &lt;strong&gt;Python&lt;/strong&gt; if you value &lt;em&gt;utility&lt;/em&gt;. Choose it if you want a tool that can do anything. Choose it if you want to work in Data Science, AI, or if you just want the safest bet for a long career.&lt;/p&gt;

&lt;p&gt;If you want to see me compare &lt;strong&gt;Go vs Rust&lt;/strong&gt; or anything else, then let me know in the comments :)&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/gYNyc3gRSDo"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>python</category>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Code Hike in 100 Seconds</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Sun, 11 Jan 2026 23:00:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/code-hike-in-100-seconds-1d9o</link>
      <guid>https://forem.com/fabianfrankwerner/code-hike-in-100-seconds-1d9o</guid>
      <description>&lt;p&gt;&lt;a href="https://codehike.org" rel="noopener noreferrer"&gt;Code Hike&lt;/a&gt;. A remark-able toolkit famous for transforming boring documentation into cinematic masterpieces. It makes your plain README files look like hot garbage by comparison. It’s the secret weapon used to build viral technical tutorials, interactive blog posts, and high-end documentation sites.&lt;/p&gt;

&lt;p&gt;It was created in 2021 by Rodrigo Pombo, a developer known for pushing the boundaries of what the web can do. He designed it to bridge the gap between static Markdown and dynamic storytelling, effectively becoming the "Christopher Nolan" of technical writing.&lt;/p&gt;

&lt;p&gt;The problem it solves is "wall-of-text" fatigue. Traditionally, if you want to explain complex code, you paste a giant block of text that readers immediately scroll past. Code Hike changes the game by treating code as a first-class citizen. Unlike standard syntax highlighters like Prism or Shiki which just colorize text, Code Hike parses the abstract syntax tree to add focus, motion, and interactivity. It allows you to guide the user's eye through a file line-by-line, creating a "scrollytelling" experience that feels more like a video than a document.&lt;/p&gt;

&lt;p&gt;To get started, install the package in a React-based framework like Next.js or Remix, then configure it as a plugin. The magic happens inside MDX files. You write standard markdown code fences, but you enhance them with magic comments.&lt;/p&gt;

&lt;p&gt;For example, write a standard JavaScript function, but inside the code block, add the comment &lt;code&gt;// ! focus&lt;/code&gt; followed by a range of line numbers. When rendered, Code Hike will spotlight those specific lines and dim the rest, ensuring the reader knows exactly where to look. You can even attach a tooltip directly to a variable (&lt;code&gt;// ! callout&lt;/code&gt;), or use the &lt;code&gt;diff&lt;/code&gt; feature to animate changes between two versions of a file.&lt;/p&gt;

&lt;p&gt;Code Hike works by compiling your markdown into fully customizable React components. This means you can swap out the default styling for your own design system, creating a consistent look across your docs. It supports everything from line numbers and copy buttons to complex tab switching and terminal simulations. Now compile your site with &lt;code&gt;npm run build&lt;/code&gt; and watch your engagement metrics hike up that mountain.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/RgRQ0HyUrH4"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>An Honest Review of Google Antigravity</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Sun, 04 Jan 2026 23:00:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/an-honest-review-of-google-antigravity-4g6f</link>
      <guid>https://forem.com/fabianfrankwerner/an-honest-review-of-google-antigravity-4g6f</guid>
      <description>&lt;p&gt;So, for the last year, if you wanted the best AI coding experience, you were paying $20 a month for Cursor. It’s been the king. But there is a new competitor in the ring. And yes, it’s a VS Code fork. But no, it’s not from some random startup.&lt;/p&gt;

&lt;p&gt;It’s from Google.&lt;/p&gt;

&lt;p&gt;It’s called Antigravity. And right now, during the preview, it is fully, 100% free. You get access to their newest, models like Gemini 3, full browser orchestration, and an entirely new way to manage code, without swiping a credit card.&lt;/p&gt;

&lt;p&gt;Now, usually when Google releases a developer tool, it’s either incredible or it gets killed in six months. I’ve been daily driving Antigravity for the past week, digging into the code, and honestly? It’s a bit of both. It is one of the most promising pieces of software I’ve seen this year, and also, at times, the single most frustrating editor I have ever used.&lt;/p&gt;

&lt;p&gt;We need to talk about the "Agent-First" workflow, the crazy Gemini 3 benchmarks, and the fact that this might actually just be a zombie version of another editor called Windsurf. Let’s get into it.&lt;/p&gt;

&lt;p&gt;So, first things first. If you peel back the skin, this is VS Code. If you go into the "About" section, you see the VS Code OSS version. But Google has done a lot of work to hide that.&lt;/p&gt;

&lt;p&gt;They’ve renamed "VS Code Settings" to just "Editor Settings." They’ve stripped out a lot of the familiar Microsoft branding. And interestingly, if you dig into the file search, you might see references to something called "Cascade."&lt;/p&gt;

&lt;p&gt;Now, if you’ve used the Windsurf editor, you know that "Cascade" is the name of their AI agent. So, it looks like Google might have acquired some tech, or some humans, or just forked a build of Windsurf to get this off the ground quickly. It’s a little uncanny valley.&lt;/p&gt;

&lt;p&gt;But the biggest change isn't the code under the hood; it's the &lt;strong&gt;Three Surfaces&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Google’s thesis is that an IDE shouldn't just be a text editor anymore. It needs to be an operating system for Agents. So you have the &lt;strong&gt;Editor&lt;/strong&gt;, which is where you type. You have the &lt;strong&gt;Browser&lt;/strong&gt;, which is a fully controlled Chrome instance. And most importantly, you have the &lt;strong&gt;Agent Manager&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This Agent Manager is the star of the show. It’s literally a separate application window that acts as an "Inbox" for your development tasks.&lt;/p&gt;

&lt;p&gt;The idea here is brilliant. Instead of having your AI chat squeezed into a sidebar inside your code, your "Manager" sits outside. It’s Mission Control. You can have five different projects running in parallel. You can see which agents are thinking, which ones are waiting for approval, and which ones have failed. It’s designed for the ADHD developer brain where you are bouncing between tasks.&lt;/p&gt;

&lt;p&gt;And when you want to dive in, you just hit Command+E, and it focuses the Editor for that specific project. It feels like a futuristic workflow... when it works. But we’ll get to the bugs in a minute. First, I want to show you what this thing can actually do when you give it a real challenge.&lt;/p&gt;

&lt;p&gt;Antigravity is built around Gemini 3 and Gemini 2.5. And since this is free right now, I decided to push it hard. I asked it to build a clone of that old "Insaniquarium" game—a simulation where you drop food, fish eat it, and they drop coins.&lt;/p&gt;

&lt;p&gt;I tried this same prompt with other high-end models like Codex High. I spent an hour fighting it. It couldn't handle the physics. The particles were broken. It burned through 3.5 million tokens and gave me a broken mess where the fish were 2D sprites floating in a void.&lt;/p&gt;

&lt;p&gt;Then I gave the exact same prompt to Antigravity with Gemini 3.&lt;/p&gt;

&lt;p&gt;It one-shot it. First try. It built a working game using Phaser. But here is the crazy part—it didn't just write the code. It generated the assets.&lt;/p&gt;

&lt;p&gt;I didn't give it images of fish or coins. The Agent realized, "Hey, I need sprites for this," so it paused, used its internal image generation model to create the fish, the food, and the background, and then injected them into the game code.&lt;/p&gt;

&lt;p&gt;Now, were they perfect? Of course not. The first time, the fish had white backgrounds instead of transparent ones, so they looked like JPEGs floating around. But I told the Agent, "Fix the transparency," and it went back, regenerated the assets, and updated the code.&lt;/p&gt;

&lt;p&gt;This is that "Agent-First" distinction. It’s not just a chatbot. It’s a worker that has access to tools—an image generator, a file system, a browser. It felt like I was directing a junior developer who also happened to be a graphic designer.&lt;/p&gt;

&lt;p&gt;The other killer feature here is the Browser integration. Google makes Chrome, Google makes Antigravity, so naturally, they talk to each other.&lt;/p&gt;

&lt;p&gt;You can spin up a "Browser Agent" to test your work. You tell it, "Go to localhost and test the game." A Chrome window opens with this blue "Agent Control" border. You watch the red dot—which is the AI's cursor—move around, click the fish, drop food, and verify the physics.&lt;/p&gt;

&lt;p&gt;It even records a video of itself doing it, so you can watch the playback later in the "Walkthrough" artifact. It captures screenshots of errors. It’s just incredibly cool to watch.&lt;/p&gt;

&lt;p&gt;But—and there is always a but—it’s buggy. Half the time, I’d get a "Controls Disabled" warning even though it was working. Sometimes it would try to connect to the wrong localhost port because I had too many projects open. It’s powerful, but it feels like a prototype.&lt;/p&gt;

&lt;p&gt;And that brings us to the reality of using Antigravity right now. As impressive as the model is, the editor itself... got some issues.&lt;/p&gt;

&lt;p&gt;First off, it’s buggy. I’ve had buttons just stop working. I’ve had the sidebar icons vanish until I clicked them blindly. The Svelte extension—one of the most popular web frameworks—just straight up doesn't work. It breaks the whole editor.&lt;/p&gt;

&lt;p&gt;Then there is the "Review Code" button. You see a button that says "Review Changes," so you click it, right?&lt;/p&gt;

&lt;p&gt;It instantly closes. It’s almost like the editor is trolling you. We joked that you just aren't in the "Agentic Mindset" yet. You don't review the code when &lt;em&gt;you&lt;/em&gt; want to; you review it when the &lt;em&gt;Agent&lt;/em&gt; tells you it's ready.&lt;/p&gt;

&lt;p&gt;There are also weird missing features. For example, arrow keys.&lt;/p&gt;

&lt;p&gt;In the file explorer, you can’t use the arrow keys to move up and down. You have to click. In a code editor. That is a crime.&lt;/p&gt;

&lt;p&gt;And for the power users: No Git Worktrees. Cursor has this, and it’s amazing for switching branches instantly. Antigravity doesn't support it yet, which is ironic because the whole point of the Agent Manager is multitasking.&lt;/p&gt;

&lt;p&gt;We also have to talk about efficiency. This app is heavy.&lt;/p&gt;

&lt;p&gt;I noticed significant battery drain on my MacBook Pro while running this. There is also this fancy glow effect around the UI when the Agent is thinking. It looks cool, but it causes input lag. When I’m typing in the editor while the Agent is generating, I can literally feel the delay.&lt;/p&gt;

&lt;p&gt;And that’s the trade-off. You are running a local Chrome instance, a heavy Electron app, and constant streaming connections to Gemini. It eats resources for breakfast.&lt;/p&gt;

&lt;p&gt;So, where does that leave us?&lt;/p&gt;

&lt;p&gt;Google Antigravity is a fascinating, frustrating, yet futuristic mess.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Agent Manager&lt;/strong&gt; workflow—that "Inbox" for your code—is genuinely a great idea. I hope every other editor copies that. The &lt;strong&gt;Gemini 3&lt;/strong&gt; model is shockingly good at one-shotting complex tasks and generating assets. And the price? Well you can't beat free.&lt;/p&gt;

&lt;p&gt;Google says the free tier limits reset every five hours, and honestly, I hit those limits a few times, but for most people, it’s plenty. You are getting access to state-of-the-art models without a subscription.&lt;/p&gt;

&lt;p&gt;But would I uninstall Cursor for this? Not today. The bugs, the missing syntax highlighting in some modes, the broken extensions—it’s just not stable enough for a deadline.&lt;/p&gt;

&lt;p&gt;However, this is just the preview. If Google actually commits to this—if they fix the bugs, bring in the full extension support, and keep the Agent Manager workflow—this could be the one.&lt;/p&gt;

&lt;p&gt;For now, I recommend you download it. Use it for your side projects. Play with the aquarium game generator. It’s a glimpse into a future where we do less typing and more managing. And that future looks pretty bright right now... assuming you can get the arrow keys to work.&lt;/p&gt;

&lt;p&gt;Are you trusting Google with your code, or are you sticking with VS Code, Cursor, or whatever?&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/4wQbY4S89Ec"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>JavaScript vs TypeScript - I built the same crypto tracker with both</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Sat, 27 Dec 2025 23:00:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/javascript-vs-typescript-i-built-the-same-crypto-tracker-with-both-3p84</link>
      <guid>https://forem.com/fabianfrankwerner/javascript-vs-typescript-i-built-the-same-crypto-tracker-with-both-3p84</guid>
      <description>&lt;p&gt;If you are building a web application today, there are two heavyweights competing for your attention. In the red corner, we have &lt;strong&gt;JavaScript&lt;/strong&gt;—the king of the web; and in the blue corner, we have &lt;strong&gt;TypeScript&lt;/strong&gt;—the challenger from Microsoft.&lt;/p&gt;

&lt;p&gt;Both tools achieve the same fundamental goal: they make things happen in a browser. But there is a world of difference in the developer experience. No matter which one you choose, the other one automatically becomes your arch-nemesis. Because you don’t choose anything but the absolute best technology out there.&lt;/p&gt;

&lt;p&gt;To figure out which one is best, I built the exact same &lt;strong&gt;Real-Time Crypto Tracker&lt;/strong&gt; with both JavaScript and TypeScript.&lt;/p&gt;

&lt;p&gt;In this post, we are going to dive into a detailed side-by-side comparison of the features, tooling, ecosystem, performance, and of course, the code! By the end of this post, you will know exactly which one fits best in your ideology.&lt;/p&gt;

&lt;p&gt;But this isn't a line-by-line coding tutorial. If you want to learn how to build these apps from scratch, &lt;a href="https://fabianfrankwerner.com/newsletter" rel="noopener noreferrer"&gt;subscribe to my free newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s start by comparing the setup and the "Barrier to Entry."&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;To get started, we are using Vite. Why Vite? Because &lt;code&gt;create-react-app&lt;/code&gt; is dead, and we don't mourn the dead here; we move on to faster tools.&lt;/p&gt;

&lt;p&gt;For JavaScript, I run &lt;code&gt;npm create vite@latest crypto-tracker-js -- --template react&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For TypeScript, it’s &lt;code&gt;npm create vite@latest crypto-tracker-ts -- --template react-ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Both are blazingly fast. Vite doesn't care about your feelings or your language preference; it just scaffolds the project instantly. But as soon as we open the projects in VS Code, the difference slaps you in the face.&lt;/p&gt;

&lt;p&gt;In the JavaScript project, you have your &lt;code&gt;package.json&lt;/code&gt;, your &lt;code&gt;vite.config.js&lt;/code&gt;, and your source files ending in &lt;code&gt;.jsx&lt;/code&gt;. It feels lightweight. You can almost hear the wind whistling through the empty space where configuration files usually live. It invites you to just start coding.&lt;/p&gt;

&lt;p&gt;Now, look at the TypeScript project. You see &lt;code&gt;.tsx&lt;/code&gt; files, which is fine, but then you see &lt;code&gt;tsconfig.json&lt;/code&gt; and &lt;code&gt;tsconfig.node.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This file is the constitution of your project. It dictates the laws of your universe. In JavaScript, the laws are "whatever works." In TypeScript, you have to explicitly tell the compiler how strict you want to be. Do you want to allow implicit &lt;code&gt;any&lt;/code&gt; types? Do you want to check for unused local variables? Do you want to strictly check for nulls?&lt;/p&gt;

&lt;p&gt;For a beginner, this file is a wall of JSON that screams "Configuration Hell." You have to understand what a "target" is and how module resolution works before you even write "Hello World." For a senior engineer, however, this file is a safety blanket. It guarantees that every developer on the team is playing by the same rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure
&lt;/h2&gt;

&lt;p&gt;The core difference between these two isn't just syntax; it's when you catch your mistakes. JavaScript is dynamically typed, which means variables can be anything at any time. TypeScript is statically typed, meaning once a variable is a number, it dies a number.&lt;/p&gt;

&lt;p&gt;Let's look at the &lt;code&gt;Coin&lt;/code&gt; component. We are fetching a list of crypto coins from an API.&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;CoinRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row"&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="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&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;&lt;/span&gt;&lt;span class="nt"&gt;span&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;coin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;span&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="nt"&gt;span&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;coin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_price&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;span&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;JavaScript&lt;/strong&gt;, I write a component that takes a prop called &lt;code&gt;data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This looks innocent. But as I'm typing &lt;code&gt;coin.current_price&lt;/code&gt;, I am relying entirely on my own memory. I have to keep the API documentation open on a second monitor. Is it &lt;code&gt;current_price&lt;/code&gt; or &lt;code&gt;currentPrice&lt;/code&gt;? Is &lt;code&gt;image&lt;/code&gt; a string URL or an object containing sizes?&lt;/p&gt;

&lt;p&gt;If I type &lt;code&gt;coin.price&lt;/code&gt; instead of &lt;code&gt;coin.current_price&lt;/code&gt;, the editor doesn't care. It saves the file. I run the app. The browser renders an empty span. I spend 15 minutes console logging &lt;code&gt;coin&lt;/code&gt; to figure out why the price is missing. This is the "Guess and Check" loop, and it burns hours of your life.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Coin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&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;symbol&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;image&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;current_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;high_24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;low_24h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, watch the &lt;strong&gt;TypeScript&lt;/strong&gt; workflow. Before I even think about the UI, I have to define the shape of my data. This is mental overhead. I have to stop, look at the API response, and translate it into an Interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Coin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Coin&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;CoinRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;coin&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;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* The moment I type "coin.", a dropdown appears */&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;span&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;coin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_price&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;span&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The editor &lt;em&gt;knows&lt;/em&gt; what a coin is. If I try to access &lt;code&gt;coin.price&lt;/code&gt;, the text turns red immediately. "Property 'price' does not exist on type 'Coin'."&lt;/p&gt;

&lt;p&gt;This is the killer feature. I am not coding in the dark anymore. The IDE is my pair programmer, constantly whispering the correct property names in my ear. It turns the documentation into code that lives right under your cursor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchCoins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.coingecko.com/...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's dial up the complexity. We need to fetch this data from the internet. This introduces asynchronous code and the concept of "Generics."&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;JavaScript&lt;/strong&gt;, our fetch function is simple, but dangerous.&lt;/p&gt;

&lt;p&gt;The return value of this function is a Promise that resolves to... &lt;code&gt;any&lt;/code&gt;. It could be an object, an array, a string, or an error message. We don't know, and JavaScript doesn't care. When we consume this function in our React component, we are flying blind.&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;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;url&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network response was not ok&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;TypeScript&lt;/strong&gt;, we want to tell the consumer of this function exactly what they are getting back. This is where we use &lt;strong&gt;Generics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This generic &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt; allows us to use this one fetch function for Coins, for User profiles, for anything, while maintaining strict typing.&lt;/p&gt;

&lt;p&gt;However, there is a dirty secret here. Look at &lt;code&gt;return data as T&lt;/code&gt;. That &lt;code&gt;as&lt;/code&gt; keyword is a lie. We are lying to the compiler. We are saying, "I promise this JSON from the internet matches my Interface."&lt;/p&gt;

&lt;p&gt;But what if the API changes? What if the internet goes down? TypeScript checks your code at &lt;em&gt;compile time&lt;/em&gt;, not &lt;em&gt;runtime&lt;/em&gt;. If the API sends back a &lt;code&gt;price&lt;/code&gt; as a string instead of a number, TypeScript won't catch it, and your app might still crash.&lt;/p&gt;

&lt;p&gt;To truly fix this, you would need a runtime validation library like &lt;strong&gt;Zod&lt;/strong&gt;. With Zod, you define a schema that validates the data &lt;em&gt;as it comes in&lt;/em&gt;, effectively bridging the gap between the static world of TypeScript and the chaotic world of runtime JavaScript. But that adds bundle size and complexity, which is the constant trade-off you make in this ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap
&lt;/h2&gt;

&lt;p&gt;Now let’s look at how we store this data in React state.&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCoins&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;JavaScript&lt;/strong&gt;, it’s simple. &lt;code&gt;coins&lt;/code&gt; is an array. I can push numbers into it, strings, objects, or even &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;coins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCoins&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Coin&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;In &lt;strong&gt;TypeScript&lt;/strong&gt;, inference usually works well, but sometimes we need to be explicit:&lt;/p&gt;

&lt;p&gt;Now, if I try to &lt;code&gt;setCoins(['bitcoin'])&lt;/code&gt;, TypeScript yells at me because a string is not a &lt;code&gt;Coin&lt;/code&gt; object. This prevents state contamination, where you accidentally store the wrong data type and break your app five steps down the line.&lt;/p&gt;

&lt;p&gt;But here is where developers get lazy. Sometimes, TypeScript is screaming at you because your types are complex. You just want it to shut up so you can test your feature. So you do the forbidden thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;The &lt;code&gt;any&lt;/code&gt; type essentially turns off TypeScript for that specific variable. It is a trap. Once you use &lt;code&gt;any&lt;/code&gt;, it spreads like a virus. If you pass that &lt;code&gt;any&lt;/code&gt; variable to another function, that function now accepts anything. You have successfully defeated the entire purpose of using TypeScript in the first place.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;any&lt;/code&gt; is like wearing a seatbelt made of paper. It looks like safety, but it won't save you when you crash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring
&lt;/h2&gt;

&lt;p&gt;If there is one reason to switch to TypeScript, it is this: Refactoring.&lt;/p&gt;

&lt;p&gt;Let's say our project manager decides that &lt;code&gt;image&lt;/code&gt; is a bad name for the property. We need to rename it to &lt;code&gt;imageUrl&lt;/code&gt; across the entire application.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;JavaScript&lt;/strong&gt; project, this is a nightmare scenario. I have to use "Find and Replace." I search for "image".&lt;/p&gt;

&lt;p&gt;The search results include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The actual property in the API response.&lt;/li&gt;
&lt;li&gt;A CSS class named &lt;code&gt;.coin-image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A comment in the README.&lt;/li&gt;
&lt;li&gt;A variable in a totally unrelated file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have to manually check every single instance. If I miss one, the image breaks. If I change the wrong one, the CSS breaks. It is anxiety-inducing.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;TypeScript&lt;/strong&gt;, I go to my Interface.&lt;/p&gt;

&lt;p&gt;I put my cursor on &lt;code&gt;image&lt;/code&gt;. I press F2 to Rename. I type &lt;code&gt;imageUrl&lt;/code&gt; and hit Enter.&lt;/p&gt;

&lt;p&gt;VS Code analyzes the dependency graph. It knows exactly which references refer to &lt;em&gt;this specific&lt;/em&gt; property on the &lt;code&gt;Coin&lt;/code&gt;interface. It ignores the CSS classes. It ignores the comments. It updates the API call, the Prop types, and the Component usage in one second.&lt;/p&gt;

&lt;p&gt;I save the file, and I have 100% confidence that I didn't break anything. This feeling of safety allows you to iterate faster. You aren't afraid to change your code because the compiler has your back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Finally, let’s talk about performance. There is a misconception that TypeScript is slower because it has to compile.&lt;/p&gt;

&lt;p&gt;Here is the reality: Browsers cannot run TypeScript. They only understand JavaScript. This means your TypeScript code must be erased before it ever reaches the user.&lt;/p&gt;

&lt;p&gt;Vite handles this using &lt;code&gt;esbuild&lt;/code&gt;. It’s incredibly fast because during development, it doesn't actually check your types. It just strips them out so the browser can run the code. This means your dev server starts almost as fast as a plain JS project.&lt;/p&gt;

&lt;p&gt;However, for the production build, we run &lt;code&gt;tsc&lt;/code&gt; (the TypeScript Compiler). This checks every single line of code for type errors. If there is a single error, the build fails.&lt;/p&gt;

&lt;p&gt;This is a feature, not a bug. It prevents you from shipping broken code to production. In JavaScript, that error would have made it to the user's browser and crashed their session. In TypeScript, it crashes your build server, forcing you to fix it.&lt;/p&gt;

&lt;p&gt;So, does TypeScript make your app faster? No. The runtime performance is identical. But does it make your &lt;em&gt;development cycle&lt;/em&gt; faster? Over the long run, absolutely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;So, after building this tracker twice, here is the verdict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt; is the wild, fun friend who invites you to a rave at 2 AM. It’s great for hackathons, small prototypes, and solo projects where you just want to move fast and break things. It feels fluid, unrestricted, and creative. If I have an idea I want to validate in an afternoon, I might still reach for JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;, however, is the responsible architect. It demands a blueprint before you lay the first brick. At first, it feels restrictive. You spend time writing Interfaces instead of features. You fight with the configuration. You curse the compiler.&lt;/p&gt;

&lt;p&gt;But as the project grows—as you add more files, more complex data structures, and more team members—TypeScript becomes the only thing keeping your sanity intact. The ability to refactor instantly, the elimination of entire classes of bugs, and the self-documenting nature of the code make it the superior choice for any serious application.&lt;/p&gt;

&lt;p&gt;If you are looking to get hired in the industry today, &lt;strong&gt;TypeScript is not optional&lt;/strong&gt;. It has won the war for attention.&lt;/p&gt;

&lt;p&gt;So, stick with JavaScript if you want to feel like a cowboy. But learn TypeScript if you want to be an engineer.&lt;/p&gt;

&lt;p&gt;If you want to see me suffer through building another app in the two programming languages or frameworks of your choice, then hit that subscribe button and let me know in the comments. Thanks for watching, and I will see you in the next one.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/Nae8rPWVLbg"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>CSS vs Tailwind CSS - I built the same home page with both</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Fri, 19 Dec 2025 11:36:07 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/css-vs-tailwind-css-i-built-the-same-home-page-with-both-23mi</link>
      <guid>https://forem.com/fabianfrankwerner/css-vs-tailwind-css-i-built-the-same-home-page-with-both-23mi</guid>
      <description>&lt;p&gt;If you go to any social media right now, I promise you will find developers screaming at each other about CSS.&lt;/p&gt;

&lt;p&gt;On one side, you have the Purists. They believe in the sanctity of semantic HTML, the Separation of Concerns, and the "Cascading" nature of style sheets.&lt;/p&gt;

&lt;p&gt;On the other side, you have the Pragmatists. The Tailwind cult. They believe that naming classes is a waste of time and that CSS is better when it looks like a crossword puzzle that exploded in your HTML.&lt;/p&gt;

&lt;p&gt;But arguments are cheap. Code is real. So, to actually understand the difference, I built the exact same project with both. We are recreating the Google Dark Mode homepage. It looks simple—a logo, a search bar, a footer—but it’s actually a perfect stress test for layout systems, theming, and component architecture.&lt;/p&gt;

&lt;p&gt;We aren't doing a "Winner takes all" battle today. We are going to dissect the experience of building this. We’ll look at the setup, the mental models, the ugly parts of the code, and finally, a file-size comparison that actually shocked me. By the end, you’ll know exactly why you should pick one over the other. Let's get into it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 1: The Setup
&lt;/h3&gt;

&lt;p&gt;Let’s start with the setup. This is where the first philosophical divide happens.&lt;/p&gt;

&lt;p&gt;With Vanilla CSS, the "stack" is non-existent. You create a file. You link it. You write code. There is zero friction. You are writing the language the browser actually understands.&lt;/p&gt;

&lt;p&gt;Tailwind, however, brings the baggage. You need Node.js. You’re initializing a config file. You’re setting up a build process to watch your files. For a single HTML page like this, Tailwind feels like bringing a construction crane to build a Lego set.&lt;/p&gt;

&lt;p&gt;But—and this is a big but—once that crane is set up, the speed changes. In Vanilla CSS, you are working in the Global Scope. The "Cascade" is powerful, but it's also dangerous. If I define a generic &lt;code&gt;button&lt;/code&gt; class, it affects every button on the site.&lt;/p&gt;

&lt;p&gt;Tailwind doesn't rely on the cascade. It relies on a System. And that system starts with configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 2: The Reset
&lt;/h3&gt;

&lt;p&gt;But before we draw a single pixel, we have to talk about the "Reset." This is Step 0, and it catches almost everyone off guard.&lt;/p&gt;

&lt;p&gt;In standard CSS, the browser uses a default box model where adding padding &lt;em&gt;increases&lt;/em&gt; the width of the element. If you have a 100px box and add 20px of padding, it becomes 140px wide. It makes layout math a nightmare.&lt;/p&gt;

&lt;p&gt;So, in my Vanilla CSS version, the very first thing I have to do is manually tell the browser: "Stop trying to help me." &lt;code&gt;border-box&lt;/code&gt; ensures that if I say a box is 100px, it stays 100px, even if I stuff it with padding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Tailwind, I didn't write a single line of reset code. Why? Because Tailwind injects a system called &lt;strong&gt;"Preflight"&lt;/strong&gt; automatically. It flattens the browser styles, applies &lt;code&gt;border-box&lt;/code&gt;globally, and removes all default margins. It gives you a truly blank canvas. In Vanilla CSS, you have to build that canvas yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 3: The Colors
&lt;/h3&gt;

&lt;p&gt;Now, let's talk about Theming. We need to match Google's specific dark mode grays.&lt;/p&gt;

&lt;p&gt;In my Vanilla CSS build, I use CSS Variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--bg-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1f1f1f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--bg-element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#303134&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--link-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#8ab4f8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is clean. It’s dynamic. If I change &lt;code&gt;--bg-primary&lt;/code&gt; to red in the DevTools, the whole site updates instantly. &lt;strong&gt;But&lt;/strong&gt;, there are no guardrails. As a developer, I can easily break the design system. So the CSS file allows me to be sloppy.&lt;/p&gt;

&lt;p&gt;In Tailwind, I have to be more disciplined. I open &lt;code&gt;tailwind.config.js&lt;/code&gt; and extend the theme.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;colors:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;google:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;bg:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#1f1f1f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;element:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#303134"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config file acts as a "Contract." When I go to my HTML, IntelliSense kicks in. If I type &lt;code&gt;bg-goo...&lt;/code&gt;, it suggests &lt;code&gt;bg-google-bg&lt;/code&gt;. It prevents "Magic Numbers." You aren't just painting pixels; you are referencing a system. For large teams, this is the difference between a consistent UI and a messy one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 4: The Layout
&lt;/h3&gt;

&lt;p&gt;Now, let's look at the actual layout code. This is where we see the concept of &lt;strong&gt;"Context Switching."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Vanilla CSS, I have this markup (&lt;code&gt;&amp;lt;div class="nav-right"&amp;gt;&lt;/code&gt;) that reads like English. But as a developer, I don't know what &lt;code&gt;.nav-right&lt;/code&gt; &lt;em&gt;does&lt;/em&gt;. Is it a grid? Is it absolute positioning? To find out, I have to switch tabs to &lt;code&gt;style.css&lt;/code&gt; and find the class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.nav-right&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&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;Okay, so this is Flexbox. We are aligning the items along the cross-axis. In Vanilla CSS, the structure and the layout logic CSS are physically separated. You have to hold the connection in your head.&lt;/p&gt;

&lt;p&gt;Now look at Tailwind. &lt;code&gt;&amp;lt;div class="nav-right flex items-center gap-4"&amp;gt;&lt;/code&gt; I admit, it looks "noisy." But look at &lt;code&gt;gap-4&lt;/code&gt;. In the old days of CSS, spacing items was hard. We used &lt;code&gt;margin-right&lt;/code&gt; on everything except the last child. Tailwind exposes the modern CSS &lt;code&gt;gap&lt;/code&gt; property as a utility. &lt;code&gt;gap-4&lt;/code&gt; puts 1rem of space between every child. I can visualize exactly how this component behaves just by reading the class string. This is &lt;strong&gt;"Locality of Behavior."&lt;/strong&gt; I am styling the element &lt;em&gt;while&lt;/em&gt; I build it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 5: The Problem
&lt;/h3&gt;

&lt;p&gt;Now, I hear the Backend Developers screaming at the screen: "But what if I have a button? Do I have to copy-paste &lt;code&gt;bg-blue-500 text-white rounded px-4 py-2&lt;/code&gt; on every single button? That violates DRY principles!" I hear you! Now, this is the "Google Search" buttons. We have two of them.&lt;/p&gt;

&lt;p&gt;In my Tailwind HTML, yes, it looks repetitive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-google-element hover:border-gray-500 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Google Search&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-google-element hover:border-gray-500 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I'm Feeling Lucky&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I want to change the padding on both, I have to edit two places. This sucks.&lt;/p&gt;

&lt;p&gt;But Tailwind has a solution for this called &lt;code&gt;@apply&lt;/code&gt;. If you really want to clean this up, you can go to your input CSS file and do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.btn-google&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;bg-google-element&lt;/span&gt; &lt;span class="err"&gt;text-google-textMain&lt;/span&gt; &lt;span class="err"&gt;px-4&lt;/span&gt; &lt;span class="err"&gt;py-2&lt;/span&gt; &lt;span class="err"&gt;rounded&lt;/span&gt; &lt;span class="py"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;border-gray-500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in your HTML, you just use &lt;code&gt;class="btn-google"&lt;/code&gt;. So you &lt;em&gt;can&lt;/em&gt; use classes in Tailwind. But usually, you shouldn't. In modern development (like with React, Vue, Svelte), you wouldn't make a CSS class. You would make a &lt;strong&gt;Component&lt;/strong&gt;. You’d make a &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; component that holds these styles internally. Therefore Tailwind works best when paired with a component framework, not just raw HTML.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 6: The Specifics
&lt;/h3&gt;

&lt;p&gt;Let's get technical. The Search Bar is the most complex component because it has specific pixel dimensions and hover states.&lt;/p&gt;

&lt;p&gt;In CSS, I handle the hover state using a Pseudo-class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.search-container&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--bg-element-hover&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This relies on &lt;strong&gt;Specificity&lt;/strong&gt;. The browser calculates that this rule is more specific than the base rule, so it applies it.&lt;/p&gt;

&lt;p&gt;In Tailwind, we do this inline. And this is where we see a feature called Arbitrary Values. Look at this class: &lt;code&gt;h-[46px]&lt;/code&gt;. And this one: &lt;code&gt;shadow-[0_1px_6px_0_rgba(23,23,23,0.5)]&lt;/code&gt;. Tailwind haters love to point at this and say, "See! That's ugly!"&lt;/p&gt;

&lt;p&gt;And yeah, it is ugly. But it’s also incredibly powerful. I needed a 46px height to match the Google reference. I didn't have to create a new class or add an inline style attribute. I just told the Tailwind Just-In-Time compiler: "Hey, make me a class for 46 pixels." And it did. I didn't have to worry about specificity wars or overriding other styles. It just works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 7: The Compiler
&lt;/h3&gt;

&lt;p&gt;Speaking of the compiler, let's get "Under the Hood." How does Tailwind actually work? When I type a class like &lt;code&gt;h-[46px]&lt;/code&gt; for the search bar, what is happening?&lt;/p&gt;

&lt;p&gt;Tailwind is not a static CSS file. It is a program written in JavaScript. When you save your HTML file, the Tailwind "Just-In-Time" (JIT) compiler scans your code. It uses Regular Expressions (Regex) to look for class names. It sees &lt;code&gt;h-[46px]&lt;/code&gt;. It parses that string, realizes you want a height of 46 pixels, and &lt;em&gt;generates&lt;/em&gt; a CSS rule on the fly: &lt;code&gt;.h-\[46px\] { height: 46px; }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is why you can leave your CSS file empty. The compiler is writing the CSS for you, based on what you asked for in the HTML. It also handles &lt;strong&gt;Specificity&lt;/strong&gt; for you. In Vanilla CSS, if I have an ID and a class, they fight. In Tailwind, because everything is a utility class, everything has the same "weight." You rarely run into those moments where you write a style and nothing changes because some hidden rule is overriding it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 8: The Paradox
&lt;/h3&gt;

&lt;p&gt;Okay, we have two identical looking websites now. But what are we shipping to the user? We always hear that "Tailwind is small." So I ran the build, and here are the results…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vanilla CSS:&lt;/strong&gt; 5 KB. &lt;strong&gt;Tailwind Output:&lt;/strong&gt; 16 KB.&lt;/p&gt;

&lt;p&gt;Wait. The "optimized" tool is &lt;strong&gt;300% larger&lt;/strong&gt;? Yes, and here is the nuance. Tailwind's 16KB includes that "Preflight" reset we talked about in Chapter 2. It’s a reset sheet that normalizes all the browser quirks so your site looks the same on Chrome and Safari. That’s a base cost. Vanilla CSS is "Pay for what you use." I only wrote the lines I needed, so it’s tiny.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However&lt;/strong&gt;, this is a single page. If I added 50 more pages to this app... The Vanilla CSS file would grow &lt;strong&gt;Linearly&lt;/strong&gt;. Every new page means new classes. The Tailwind file would flatten out (Logarithmic). Why? Because I’m reusing &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;items-center&lt;/code&gt;, and &lt;code&gt;text-white&lt;/code&gt; everywhere. So while Vanilla wins the sprint, Tailwind wins the marathon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 9: The Verdict
&lt;/h3&gt;

&lt;p&gt;So, which one should you choose?&lt;/p&gt;

&lt;p&gt;If you are a beginner, look me in the eyes: &lt;strong&gt;Do not start with Tailwind.&lt;/strong&gt; You need to feel the pain of the Box Model. You need to understand how Flexbox axes work. You need to understand &lt;code&gt;position: relative&lt;/code&gt; vs &lt;code&gt;absolute&lt;/code&gt;. If you jump straight to Tailwind, you are learning a framework, not the web.&lt;/p&gt;

&lt;p&gt;But, if you are building a real product, a dashboard, or working on a team? &lt;strong&gt;Tailwind CSS&lt;/strong&gt; has won me over. The "ugliness" of the HTML is a small price to pay for the development speed. It allows you to come back to a project 6 months later and know exactly what is happening without hunting through a 5,000-line stylesheet.&lt;/p&gt;

&lt;p&gt;And if you want to see me compare &lt;strong&gt;React vs Svelte&lt;/strong&gt; or something else entirely, let me know in the comments. Don't forget to &lt;code&gt;cursor: pointer&lt;/code&gt; that like button. See you in the next one.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/Owzd1fLJFxY"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tailwindcss</category>
      <category>beginners</category>
    </item>
    <item>
      <title>OpenAI's Browser is here… and 7 more things that shipped this week</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Fri, 31 Oct 2025 12:35:26 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/openais-browser-is-here-and-7-more-things-that-shipped-this-week-p2c</link>
      <guid>https://forem.com/fabianfrankwerner/openais-browser-is-here-and-7-more-things-that-shipped-this-week-p2c</guid>
      <description>&lt;p&gt;It’s time for another round of tech news.&lt;/p&gt;

&lt;p&gt;OpenAI released Atlas.&lt;/p&gt;

&lt;p&gt;A full-fledged web browser that bakes ChatGPT right into the interface.&lt;/p&gt;

&lt;p&gt;It’s designed to let you browse, search, and summarize pages inside the same window, powered by GPT-4o-mini.&lt;/p&gt;

&lt;p&gt;Imagine Chrome, but every tab comes with its own assistant.&lt;/p&gt;

&lt;p&gt;It’s Mac-only for now, with Windows coming later, and yes, it uses Chromium under the hood.&lt;/p&gt;

&lt;p&gt;But not everything’s shiny — early testers already found that the browser might be more vulnerable than Chrome or Edge because of how its sandboxing works.&lt;/p&gt;

&lt;p&gt;So yeah, it’s cool… but maybe don’t log into your bank account just yet.&lt;/p&gt;

&lt;p&gt;Next, React Native 0.82.&lt;/p&gt;

&lt;p&gt;After seven years of work — yes, seven — the React Native team has finally shipped the New Architecture.&lt;/p&gt;

&lt;p&gt;It’s a total rebuild of how React Native talks to your phone’s native layer.&lt;/p&gt;

&lt;p&gt;Gone is the old JSON bridge that caused delays and random jank.&lt;/p&gt;

&lt;p&gt;Now, JavaScript and native code communicate directly through a new C++ API.&lt;/p&gt;

&lt;p&gt;You also get concurrent rendering and an event loop that can handle multiple priorities, meaning smoother UIs and fewer dropped frames.&lt;/p&gt;

&lt;p&gt;And there’s a new version of the Hermes engine, with huge performance gains.&lt;/p&gt;

&lt;p&gt;Then there’s Next.js 16.&lt;/p&gt;

&lt;p&gt;This version makes Turbopack the default bundler, adds new caching APIs, and integrates seamlessly with the React Compiler.&lt;/p&gt;

&lt;p&gt;It’s faster, leaner, and breaks just enough things to remind you it’s still JavaScript.&lt;/p&gt;

&lt;p&gt;And speaking of JavaScript, Node… version 25 came out.&lt;/p&gt;

&lt;p&gt;It ships with the V8 14.1 engine, faster JSON.stringify, built-in base64 helpers, and safer defaults for networking.&lt;/p&gt;

&lt;p&gt;Not as flashy as Bun, but this is the foundation most of the web still runs on — so those boring updates matter.&lt;/p&gt;

&lt;p&gt;Now, let’s talk about the new kid on the block — Vite+.&lt;/p&gt;

&lt;p&gt;The team behind Vite, Vitest, and Rolldown announced &lt;em&gt;Vite Plus&lt;/em&gt; — a unified toolchain that does everything:&lt;/p&gt;

&lt;p&gt;develop, build, test, lint, and even visualize your project, all from one CLI written in Rust.&lt;/p&gt;

&lt;p&gt;So instead of duct-taping five tools together, you just run &lt;em&gt;vite new&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It’s still source-available and free for individuals, but the premium features will help fund the ecosystem.&lt;/p&gt;

&lt;p&gt;Honestly, I love this move — finally an open-source company that wants to make money &lt;em&gt;without&lt;/em&gt; turning into another cloud platform.&lt;/p&gt;

&lt;p&gt;In framework news, Astro 5.15 dropped with smarter font preloads and deployment fixes.&lt;/p&gt;

&lt;p&gt;Solito 5 brings better support for hybrid Next + React Native apps.&lt;/p&gt;

&lt;p&gt;Biome 2.3 improves linting speed and stability.&lt;/p&gt;

&lt;p&gt;And the Hono CLI now scaffolds full-stack apps in seconds.&lt;/p&gt;

&lt;p&gt;Cursor 2 also launched this week, and it’s not just an editor update.&lt;/p&gt;

&lt;p&gt;It’s trying to become &lt;em&gt;the&lt;/em&gt; AI IDE — think Copilot, but deeply integrated into your entire workflow.&lt;/p&gt;

&lt;p&gt;You can ask questions, refactor code, and spin up full projects without leaving your editor.&lt;/p&gt;

&lt;p&gt;And then there’s Workflow.&lt;/p&gt;

&lt;p&gt;It’s a new TypeScript toolkit that makes your async code durable.&lt;/p&gt;

&lt;p&gt;Basically, you can take any regular async function, add "use workflow", and it suddenly becomes reliable, resumable, and observable — no queues, no YAML, no ops nightmares.&lt;/p&gt;

&lt;p&gt;Think of it as “reliability-as-code.” You write normal TypeScript, and Workflow handles retries, state persistence, and tracing automatically.&lt;/p&gt;

&lt;p&gt;You can even pause for days or months, come back, and pick up right where you left off — perfect for long-running jobs or AI agents that need memory.&lt;/p&gt;

&lt;p&gt;There’s built-in observability too, so you can replay, debug, and time-travel through your workflow runs.&lt;/p&gt;

&lt;p&gt;It works locally, in Docker, or on Vercel — totally open source and portable.&lt;/p&gt;

&lt;p&gt;That’s all for this week...&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/ZSjkVTIfiBM"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>10 Dead Programming Languages</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Wed, 15 Oct 2025 22:00:14 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/10-dead-programming-languages-3mhb</link>
      <guid>https://forem.com/fabianfrankwerner/10-dead-programming-languages-3mhb</guid>
      <description>&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/93NRBjN6L-E"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Forget your toy languages, such as Python and JavaScript.&lt;/p&gt;

&lt;p&gt;We’re grave robbing ten programming languages that have been dead for decades.&lt;/p&gt;

&lt;p&gt;Like COBOL, which was born in 1960 when the US Department of Defense decided business software should all speak one language. The result? A &lt;em&gt;committee-approved&lt;/em&gt; monstrosity designed to manage payroll, inventory, and crush the souls of programmers.&lt;/p&gt;

&lt;p&gt;At a time when FORTRAN and ALGOL only had arrays, it introduced record data structures—the precursor to structs.&lt;/p&gt;

&lt;p&gt;It died because academia hated it; it was massive and slow, and no one wanted to build on top of it.&lt;/p&gt;

&lt;p&gt;Yet, COBOL still runs &lt;em&gt;a shocking amount&lt;/em&gt; of global finance, insurance, and government systems. Your bank statement probably comes from a “dead” language older than your parents.&lt;/p&gt;

&lt;p&gt;ALGOL, released in 1960, was supposed to be pseudocode you could actually run. And it invented half of modern programming: lexical scoping, structured programming, nested functions, formal specs, block comments—the works.&lt;/p&gt;

&lt;p&gt;It’s the reason most modern languages &lt;em&gt;look&lt;/em&gt; the way they do. C? Java? JavaScript? All ALGOL descendants.&lt;/p&gt;

&lt;p&gt;It died because they forgot to include I/O. You couldn’t use it for… you know… &lt;em&gt;anything practical&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Instead, people made “ALGOL-like” languages with actual features—like BCPL, which inspired C, which inspired everything else. ALGOL’s kids buried it alive.&lt;/p&gt;

&lt;p&gt;APL came out in 1962 and looked like someone spilled a math textbook on your keyboard.&lt;/p&gt;

&lt;p&gt;Seriously—you needed a special keyboard just to type it. But under the cryptic symbols was a revolutionary idea: array programming. No more looping through lists—just tell the computer to operate on the whole thing at once.&lt;/p&gt;

&lt;p&gt;APL’s DNA runs through R, NumPy, pandas, MATLAB, and most of modern data science.&lt;/p&gt;

&lt;p&gt;It died because of the keyboard, for one. Also, it only worked well with homogeneous data, so no fancy dataframes.&lt;/p&gt;

&lt;p&gt;BASIC arrived in 1964 to make computers accessible to “non-engineers”, meaning kids, hobbyists, and small businesses.&lt;/p&gt;

&lt;p&gt;One the 1970s microcomputers it was &lt;em&gt;the&lt;/em&gt; language—you could cram a BASIC interpreter into just 2 KB of RAM.&lt;/p&gt;

&lt;p&gt;BASIC gave us real-time interpreters and an entire generation of self-taught programmers. Eventually, it evolved into Visual Basic, the language behind countless Office macros.&lt;/p&gt;

&lt;p&gt;It died, because people looked down on it as a “toy” language. Once cheap hardware could run C, BASIC was pushed aside.&lt;/p&gt;

&lt;p&gt;In 1966, IBM tried to merge FORTRAN and COBOL into a single “super language” for both science and business. They created PL/I—the Swiss Army knife of the 60s.&lt;/p&gt;

&lt;p&gt;It had structured types before C, pointers before C, constants, function overloading—you name it.&lt;/p&gt;

&lt;p&gt;It died because everyone hated it for different reasons. FORTRAN people thought it was too COBOL-like, COBOL people thought it was too FORTRAN-like, and everyone else thought IBM was trying to lock them in.&lt;/p&gt;

&lt;p&gt;SIMULA 67, born in Norway, started as a simulation language but became the first real object-oriented programming language—complete with classes, subclassing, virtual methods, and protected attributes.&lt;/p&gt;

&lt;p&gt;It inspired C++, CLU, ML, and basically every OOP textbook ever written.&lt;/p&gt;

&lt;p&gt;It died because it was painfully slow. Like, “hope you’re running on a mainframe” slow. People stole its ideas and reimplemented them in faster, more practical languages.&lt;/p&gt;

&lt;p&gt;Niklaus Wirth created Pascal in 1970 as a clean, simple alternative to the overly complicated ALGOL-68. It became &lt;em&gt;the&lt;/em&gt; teaching language of the 70s and 80s—structured, readable, and widely adopted in universities.&lt;/p&gt;

&lt;p&gt;It died, because it just… slowly faded. C became cooler, Java took over academia, and Pascal became that nice guy you remember fondly but never call.&lt;/p&gt;

&lt;p&gt;CLU, from 1975, was basically the hipster of programming languages—full of ideas no one appreciated until decades later: abstract data types, iterators, generics, checked exceptions.&lt;/p&gt;

&lt;p&gt;It died because no one cared about its concepts at the time; therefore, CLU quietly slipped into history.&lt;/p&gt;

&lt;p&gt;ML started in 1976 as the language for the LCF proof assistant. It introduced type inference, meaning you didn’t have to write out all your types—the compiler just knew.&lt;/p&gt;

&lt;p&gt;It also championed typed functional programming, algebraic data types, and modularity.&lt;/p&gt;

&lt;p&gt;It died, because it stayed niche. By the time Standard ML arrived, Haskell was stealing the functional programming spotlight. Its descendants live on in OCaml and F#.&lt;/p&gt;

&lt;p&gt;Smalltalk evolved from 1972 to 1980 and was the first language to make &lt;em&gt;everything&lt;/em&gt; an object.&lt;/p&gt;

&lt;p&gt;It defined much of how we think about OOP and directly inspired modern IDEs and UI frameworks.&lt;/p&gt;

&lt;p&gt;It died because of the “Javapocalypse.” In the 90s, Java stormed in with its C-like syntax, better C interop, and massive corporate backing.&lt;/p&gt;

&lt;p&gt;But actually, none of these languages is truly gone. Their DNA is everywhere—in the languages you use today!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>learning</category>
      <category>computerscience</category>
      <category>beginners</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Mon, 11 Aug 2025 19:33:01 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/-1l89</link>
      <guid>https://forem.com/fabianfrankwerner/-1l89</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/fabianfrankwerner/how-to-actually-learn-javascript-a-roadmap-for-beginners-a2n" class="crayons-story__hidden-navigation-link"&gt;How to ACTUALLY learn JavaScript... A roadmap for beginners&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/fabianfrankwerner" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F2664526%2Fd7b28645-176d-4e7d-968a-7dbba17c6bf7.png" alt="fabianfrankwerner profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/fabianfrankwerner" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Fabian Frank Werner
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Fabian Frank Werner
                
              
              &lt;div id="story-author-preview-content-2757558" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/fabianfrankwerner" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F2664526%2Fd7b28645-176d-4e7d-968a-7dbba17c6bf7.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Fabian Frank Werner&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/fabianfrankwerner/how-to-actually-learn-javascript-a-roadmap-for-beginners-a2n" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 7 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/fabianfrankwerner/how-to-actually-learn-javascript-a-roadmap-for-beginners-a2n" id="article-link-2757558"&gt;
          How to ACTUALLY learn JavaScript... A roadmap for beginners
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/learning"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;learning&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/fabianfrankwerner/how-to-actually-learn-javascript-a-roadmap-for-beginners-a2n" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;13&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/fabianfrankwerner/how-to-actually-learn-javascript-a-roadmap-for-beginners-a2n#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              8&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Inner Join vs Left Join vs Right Join vs Full Join</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Mon, 11 Aug 2025 09:57:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/inner-join-vs-left-join-vs-right-join-vs-full-join-3fhd</link>
      <guid>https://forem.com/fabianfrankwerner/inner-join-vs-left-join-vs-right-join-vs-full-join-3fhd</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/l96afgBCWAE"&gt;
  &lt;/iframe&gt;
&lt;br&gt;
You might’ve heard terms like &lt;em&gt;inner join&lt;/em&gt;, &lt;em&gt;left join&lt;/em&gt;, or &lt;em&gt;full outer join&lt;/em&gt; thrown around like confetti at an SQL data party.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And if you’re anything like me when I first heard them…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You nodded along while secretly wondering if these were moves from a ballroom dance class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So let’s break them down — clearly, visually, and with as little jargon as possible!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We’ll focus on the Big Four: INNER, LEFT, RIGHT, and FULL joins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the end of this post, you'll know exactly which one to reach for and when.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And we’ll even sprinkle in the &lt;em&gt;fancy&lt;/em&gt; joins for bonus street cred.&lt;/p&gt;
&lt;h2&gt;
  
  
  But first… What’s a Join?
&lt;/h2&gt;

&lt;p&gt;Imagine two tables at a party.&lt;/p&gt;

&lt;p&gt;Table A: A list of customers.&lt;/p&gt;

&lt;p&gt;Table B: A list of orders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each table knows its stuff—but they’re socially awkward and don’t talk to each other unless you explicitly tell them to.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s what a join does: it introduces one table to another using a common key (usually an ID).&lt;/p&gt;

&lt;p&gt;So, SQL joins let you combine rows from two (or more) tables based on a related column. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sounds simple? Good. Let’s get messy.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Inner Join
&lt;/h2&gt;

&lt;p&gt;If SQL joins were a Venn diagram, the &lt;strong&gt;INNER JOIN&lt;/strong&gt; would be the part where the two circles overlap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says: “Hey database, give me everyone who exists in both the &lt;em&gt;customers&lt;/em&gt; and &lt;em&gt;orders&lt;/em&gt; table.” So, if someone placed an order, they show up. If they haven’t? Tough luck—they’re not invited to this query party.&lt;/p&gt;

&lt;p&gt;It’s clean. It’s efficient. It’s what most people use by default because it filters out the data that doesn’t match on both sides.&lt;/p&gt;

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

&lt;p&gt;✅ You only care about matches in both tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Left Join
&lt;/h2&gt;

&lt;p&gt;Now, the &lt;strong&gt;LEFT JOIN&lt;/strong&gt; is like a teacher who wants to make sure every student in their class gets called on—even if some don’t have answers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says: “Give me &lt;em&gt;all&lt;/em&gt; customers, and show their orders &lt;em&gt;if they have any&lt;/em&gt;.” If a customer exists but hasn’t placed an order, they’ll still be in the results—with &lt;code&gt;NULL&lt;/code&gt; in the columns from the &lt;em&gt;orders&lt;/em&gt; table.&lt;/p&gt;

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

&lt;p&gt;✅ You care about everyone in the left table (usually your main dataset).&lt;/p&gt;

&lt;p&gt;✅ You want to see who’s missing data on the other side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Right Join
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RIGHT JOIN&lt;/strong&gt; is the LEFT JOIN’s mirror twin. It’s clingy to the &lt;em&gt;right&lt;/em&gt; table instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says: “Give me &lt;em&gt;all&lt;/em&gt; orders, and show the customer info &lt;em&gt;if it exists&lt;/em&gt;.” This can be helpful in rare cases where the right table is the primary one you care about.&lt;/p&gt;

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

&lt;p&gt;✅ You care more about the second table than the first.&lt;/p&gt;

&lt;p&gt;Honestly? You can do everything RIGHT JOIN does with a LEFT JOIN if you just flip the table order. So use it if you must—but only if you enjoy chaos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Join
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;FULL OUTER JOIN&lt;/strong&gt; is the SQL equivalent of Oprah giving out participation medals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;FULL&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says: “Give me &lt;em&gt;all&lt;/em&gt; customers AND &lt;em&gt;all&lt;/em&gt; orders—even if there’s no match between them.”&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Customers with orders ✅&lt;/li&gt;
&lt;li&gt;Orders with customers ✅&lt;/li&gt;
&lt;li&gt;Customers without orders ✅&lt;/li&gt;
&lt;li&gt;Orders without customers ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The unmatched rows will have &lt;code&gt;NULL&lt;/code&gt; values on whichever side didn’t find a match.&lt;/p&gt;

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

&lt;p&gt;✅ You don’t want to miss anything.&lt;/p&gt;

&lt;p&gt;✅ You’re doing audits, reconciliations, or just being overly inclusive.&lt;/p&gt;

&lt;p&gt;Here’s how it looks in diagram brain terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;INNER JOIN&lt;/strong&gt; = Middle only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LEFT JOIN&lt;/strong&gt; = Left + middle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RIGHT JOIN&lt;/strong&gt; = Right + middle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FULL OUTER JOIN&lt;/strong&gt; = Everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if you ever forget which is which, imagine two overlapping pizza slices and ask: “How much pizza do I want?” The answer determines the join, I guess…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now, if you’ve mastered the basics and want to look smarter at your SQL database parties, here are a few fancier Joins to casually drop into conversation:&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Self Join
&lt;/h2&gt;

&lt;p&gt;When a table joins &lt;em&gt;itself&lt;/em&gt;—great for hierarchical data like org charts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Cross Join
&lt;/h2&gt;

&lt;p&gt;Every row from one table joins with &lt;em&gt;every&lt;/em&gt; row from the other. Usually results in a Cartesian explosion. This is great for permutations, terrible for performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sizes&lt;/span&gt;
&lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Natural Join
&lt;/h2&gt;

&lt;p&gt;SQL tries to guess the join condition for you. Which sounds nice... until it guesses wrong!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;And always test your joins with real data to make sure you're not accidentally excluding or duplicating anything.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>tutorial</category>
      <category>sql</category>
      <category>learning</category>
    </item>
    <item>
      <title>How to ACTUALLY learn JavaScript... A roadmap for beginners</title>
      <dc:creator>Fabian Frank Werner</dc:creator>
      <pubDate>Thu, 07 Aug 2025 10:17:00 +0000</pubDate>
      <link>https://forem.com/fabianfrankwerner/how-to-actually-learn-javascript-a-roadmap-for-beginners-a2n</link>
      <guid>https://forem.com/fabianfrankwerner/how-to-actually-learn-javascript-a-roadmap-for-beginners-a2n</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/hIlgGTyyDmk"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;An excellent salary, back problems, and social isolation…&lt;/p&gt;

&lt;p&gt;If that sounds good to you, I recommend learning the JavaScript programming language.&lt;/p&gt;

&lt;p&gt;Learning to code was the best investment in my life.&lt;/p&gt;

&lt;p&gt;And it cost me approximately $0, thanks to all the free resources available on the internet.&lt;/p&gt;

&lt;p&gt;But how does one learn JavaScript as a complete beginner?&lt;/p&gt;

&lt;p&gt;Luckily, you don’t need to be very smart, but you do need to work very hard. And put in the hours required to learn JavaScript as a usable skill.&lt;/p&gt;

&lt;p&gt;But hours doing what exactly? Where do I even start?&lt;/p&gt;

&lt;p&gt;Sadly, there is no guaranteed step-by-step program, but luckily, there is this post giving you a beginner roadmap to learn JavaScript step by step.&lt;/p&gt;

&lt;p&gt;I’m going to blitz through the key JavaScript topics…&lt;/p&gt;

&lt;p&gt;Giving you a big‑picture overview and the right order to tackle them later in your own self‑guided deep dive.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is JavaScript?
&lt;/h2&gt;

&lt;p&gt;JavaScript is the language that powers everything from flashy websites to full‑blown server apps, and occasionally, your late‑night debugging-induced existential crisis. It’s the only programming language that works in every browser, meaning if you’ve clicked, scrolled, or been annoyed by a pop‑up, you’ve met JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief (and Weird) History of JavaScript
&lt;/h2&gt;

&lt;p&gt;JavaScript has been through more name changes than a wannabe pop star. It started as &lt;em&gt;Mocha&lt;/em&gt;, then got rebranded to &lt;em&gt;LiveScript&lt;/em&gt;, before Netscape slapped on “JavaScript” to ride the hype wave of Java… which is ironic, because JavaScript and Java have about as much in common as hamsters and ham radios.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Run JavaScript
&lt;/h2&gt;

&lt;p&gt;Want to run JavaScript? Easy. Stick it in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag in your HTML, type it straight into the browser console, or—if you like your code without a browser babysitter—fire it up in Node.js. There are fancier ways, but this will get you from “I have code” to “look, it runs!” in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scopes (a.k.a. Where Your Variables Live and Die)
&lt;/h2&gt;

&lt;p&gt;Variables are just boxes for storing values, and in JavaScript, &lt;em&gt;where&lt;/em&gt; you declare them determines &lt;em&gt;who&lt;/em&gt; can see them. There’s global scope (everyone can see it), local scope (only certain parts can), function scope, and block scope. Before 2015, we only had &lt;code&gt;var&lt;/code&gt;, which was… fine, but also cursed. Then &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; showed up, giving us sane block scoping and making &lt;code&gt;var&lt;/code&gt; the VHS tape of JavaScript—still playable, but why would you?&lt;/p&gt;

&lt;h2&gt;
  
  
  Datatypes
&lt;/h2&gt;

&lt;p&gt;JavaScript has seven primitive datatypes—Number, BigInt, String, Boolean, Null, Undefined, and Symbol. Everything else? Objects. Arrays? Objects. Functions? Objects. Your hopes and dreams? …Still objects. You can check a variable’s type with &lt;code&gt;typeof&lt;/code&gt;, but beware: &lt;code&gt;typeof null&lt;/code&gt; says “object” because JavaScript likes to keep some of its weird 1995 quirks alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implicit Type Casting (a.k.a. “JavaScript Will Guess for You”)
&lt;/h2&gt;

&lt;p&gt;JavaScript loves to “help” by converting types behind your back. Sometimes it’s convenient, sometimes it’s a horror show. &lt;code&gt;1 + '1'&lt;/code&gt;? That’s &lt;code&gt;'11'&lt;/code&gt;. &lt;code&gt;1 - '1'&lt;/code&gt;? That’s &lt;code&gt;0&lt;/code&gt;. Why? Don’t ask. This is one reason TypeScript exists: to stop JavaScript from playing guessing games with your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyed Collections
&lt;/h2&gt;

&lt;p&gt;Beyond plain objects, JavaScript has &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;WeakMap&lt;/code&gt;, &lt;code&gt;Set&lt;/code&gt;, and &lt;code&gt;WeakSet&lt;/code&gt;. Maps let you store key/value pairs with any type of key. Sets are just unique collections of values. The “weak” versions? They don’t prevent garbage collection, so they’re good for memory‑sensitive data you don’t want hanging around forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON
&lt;/h2&gt;

&lt;p&gt;JSON — Not a person. Not a typo. It stands for JavaScript Object Notation and is a lightweight data format for sending info between computers. It’s just strings that look like objects, and it’s everywhere: APIs, configs, and anything that needs to speak “structured text.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Value Comparison Operators
&lt;/h2&gt;

&lt;p&gt;JavaScript gives you &lt;code&gt;==&lt;/code&gt; (equality) and &lt;code&gt;===&lt;/code&gt; (strict equality). &lt;code&gt;==&lt;/code&gt; will happily convert types before comparing, which can lead to “fun” surprises. &lt;code&gt;===&lt;/code&gt; does a strict check for both value and type—generally the safer choice. Then there’s &lt;code&gt;Object.is()&lt;/code&gt;, which is even stricter, because of course, we needed &lt;em&gt;three&lt;/em&gt; ways to compare values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loops and Iterations
&lt;/h2&gt;

&lt;p&gt;If you need to repeat something, you’ve got &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;do...while&lt;/code&gt;, &lt;code&gt;for...in&lt;/code&gt;, &lt;code&gt;for...of&lt;/code&gt;, plus &lt;code&gt;break&lt;/code&gt; to stop early and &lt;code&gt;continue&lt;/code&gt; to skip steps. If your brain doesn’t hurt after learning them all, congratulations—you’re ready to write JavaScript that confuses future you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional Statements
&lt;/h2&gt;

&lt;p&gt;If you need your code to make decisions, you’ll use &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;if...else&lt;/code&gt;, or &lt;code&gt;switch&lt;/code&gt;. The first two are straightforward. &lt;code&gt;switch&lt;/code&gt; is for when you have multiple cases to check and want your code to look like a retro telephone switchboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try, Catch, Finally
&lt;/h2&gt;

&lt;p&gt;When things break—and they &lt;em&gt;will&lt;/em&gt;—wrap your code in &lt;code&gt;try&lt;/code&gt; to run it, &lt;code&gt;catch&lt;/code&gt; to handle errors, and &lt;code&gt;finally&lt;/code&gt; for code that runs no matter what. Perfect for cleanup, logging, or making sarcastic comments after everything explodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilizing Error Objects
&lt;/h2&gt;

&lt;p&gt;When JavaScript throws an error, it hands you an &lt;code&gt;Error&lt;/code&gt; object. You can inspect it to figure out what went wrong, then decide whether to fix it, log it, or just pretend it never happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expressions and Operators
&lt;/h2&gt;

&lt;p&gt;Everything from &lt;code&gt;+&lt;/code&gt; (plus) to &lt;code&gt;===&lt;/code&gt; (strict equality) to &lt;code&gt;??&lt;/code&gt; (these things) fall under expressions and operators. There are a &lt;em&gt;lot&lt;/em&gt; of them: arithmetic, logical, assignment, comparison, bitwise… and a few that only show up to ruin your day if you forget how they work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functions
&lt;/h2&gt;

&lt;p&gt;Functions are reusable chunks of code you can call whenever you want. They take optional parameters, return optional values, and sometimes cause non-optional headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arrow Functions
&lt;/h2&gt;

&lt;p&gt;The arrow function is the hipster version of functions—shorter syntax, no own &lt;code&gt;this&lt;/code&gt; binding, and looks cooler.&lt;/p&gt;

&lt;h2&gt;
  
  
  IIFE (Immediately Invoked Function Expression)
&lt;/h2&gt;

&lt;p&gt;IIFEs are functions that run as soon as you define them. This is great for quick, one‑off code execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursion
&lt;/h2&gt;

&lt;p&gt;Recursion is when a function calls itself until it hits a “base case” and stops. Use it for elegant problem solving… or infinite loops if you mess it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closures
&lt;/h2&gt;

&lt;p&gt;A closure is when a function remembers the variables from the place it was created, even after that place is gone. It’s like a function with a secret backpack of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built‑in Functions
&lt;/h2&gt;

&lt;p&gt;Stuff like &lt;code&gt;parseInt()&lt;/code&gt;, &lt;code&gt;setTimeout()&lt;/code&gt;, and &lt;code&gt;Math.random()&lt;/code&gt;—plus methods on &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, and &lt;code&gt;Date&lt;/code&gt;—are ready to use without writing them yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  DOM APIs
&lt;/h2&gt;

&lt;p&gt;JavaScript can poke, prod, and completely rearrange your web page via the DOM. Change text, update styles, remove elements, add elements—it’s all fair game.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;this&lt;/code&gt; Keyword
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;this&lt;/code&gt; changes depending on how and where you use it. Sometimes it’s the object you’re in, sometimes it’s the global object, and sometimes it’s &lt;code&gt;undefined&lt;/code&gt;—because JavaScript loves to keep you guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous JavaScript
&lt;/h2&gt;

&lt;p&gt;Some tasks—like fetching data or using a camera—take time, so JavaScript handles them asynchronously. That means your code can keep running while it waits for results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Callbacks, Callback Hell, and Async/Await
&lt;/h2&gt;

&lt;p&gt;Callbacks are functions passed into other functions. Too many nested callbacks turn your code into “callback hell”—a pyramid of doom. Async/await fixes that by making asynchronous code look synchronous, which your future self will thank you for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetch
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fetch()&lt;/code&gt; lets you grab data from servers and APIs. It returns a promise, which you can &lt;code&gt;await&lt;/code&gt; like the cool kids.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modules
&lt;/h2&gt;

&lt;p&gt;Split your code into separate files, export what you need, and import it where you need it. Modules keep things organized and your sanity intact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chrome DevTools &amp;amp; Debugging
&lt;/h2&gt;

&lt;p&gt;Every browser has dev tools for inspecting HTML &amp;amp; CSS, running JS in the console, setting breakpoints, and checking performance. Chrome’s Lighthouse can even rate your website—though be prepared for a brutal honesty session.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now it’s your turn: pick a project, start building, break things, fix them, and repeat until you accidentally become a JavaScript developer.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
