<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>https://www.hassandev.me</loc>
<lastmod>2026-10-15</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/about</loc>
<lastmod>2026-10-15</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/work</loc>
<lastmod>2026-10-15</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/contact</loc>
<lastmod>2026-10-15</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/privacy</loc>
<lastmod>2026-10-15</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/designing-websites</loc>
<lastmod>2026-10-15</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/services/ai-automation</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/work/jumprope-tracker</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/work/mastersealer</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/work/phantom-editor</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog</loc>
<lastmod>2026-10-16T00:00:00.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/series/redis-clone-in-go</loc>
<lastmod>2025-12-09T06:13:10.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/series/google-drive-sync</loc>
<lastmod>2026-01-04T13:05:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/series/landing-page-design-nextjs-tailwind</loc>
<lastmod>2025-04-20T15:26:16.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/series/automation-tips</loc>
<lastmod>2026-08-24T17:34:10.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/series/memory-management-in-go</loc>
<lastmod>2025-10-22T08:03:24.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/series/self-hosted-loom</loc>
<lastmod>2026-09-04T16:28:44.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/ai-automation-for-small-business</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/what-to-automate-first</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/ai-agents-vs-workflows</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/reliable-ai-automations</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/ai-automation-cost</loc>
<lastmod>2026-10-16</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/the-best-engineers-are-curious</loc>
<lastmod>2026-07-23</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/i-dont-care-if-you-use-ai</loc>
<lastmod>2026-07-19</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-i-would-learn-to-code-in-2026-GloEbjj45Oo</loc>
<video:video>
<video:title>How I Would Learn To Code In 2026</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/GloEbjj45Oo/hqdefault.jpg</video:thumbnail_loc>
<video:description>I would learn to code in 2026 by building real projects that solve real problems while letting AI write most of the code, then constantly asking the AI to explain what it wrote until I understand the architecture, implementation, and tradeoffs. Coding is not solved, and learning is not obsolete: if you only prompt, you abstract away the mistakes that teach you how software actually works. AI massively amplifies senior developers but can reduce junior productivity, because juniors lack the prerequisite knowledge to prompt for good architecture, performance, and security. I use my 19,000-line AI-generated Phantom Editor codebase as the case study: I had no idea how FFmpeg, browser automation, or video/audio processing worked, but by asking endless questions and optimizing a slow video-encoding pipeline, I learned those areas deeply. My take is simple: skip memorizing syntax, build a lot, and focus on software engineering fundamentals.</video:description>
<video:player_loc>https://www.youtube.com/embed/GloEbjj45Oo</video:player_loc>
<video:duration>769</video:duration>
<video:publication_date>2026-10-14T15:33:52.000Z</video:publication_date>
</video:video>
<lastmod>2026-10-14T15:33:52.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/optimizing-ai-generated-codebases-how-i-do-it-IOBacwR4uPA</loc>
<video:video>
<video:title>Optimizing AI Generated Codebases - How I Do It</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/IOBacwR4uPA/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI-generated codebases run slow because each abstraction layer—including AI—trades raw performance for ease, so I walk through how I optimize mine by learning common bottlenecks before prompting an agent. I cover load balancing, parallelization, efficient algorithms, and why hash-map lookups are O(1) versus O(n) list scans. Then I apply those ideas to my Phantom Editor pipeline: encode video once instead of after every step, split audio into 60-second chunks so DeepFilter noise reduction uses all four CPU cores, trim silence before audio processing, fire transcription requests to Grok/Whisper concurrently, and standardize FFmpeg parameters. My take: AI won&apos;t optimize your app unless you understand the bottlenecks and prompt it with specific fixes.</video:description>
<video:player_loc>https://www.youtube.com/embed/IOBacwR4uPA</video:player_loc>
<video:duration>968</video:duration>
<video:publication_date>2026-10-11T17:34:38.000Z</video:publication_date>
</video:video>
<lastmod>2026-10-11T17:34:38.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/what-to-automate-_v1qwRWnWZY</loc>
<video:video>
<video:title>What To Automate?</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/_v1qwRWnWZY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to decide what to automate by tracking your work for a week instead of brainstorming, because most people pick tasks that either take longer to automate than to do manually or have no real value. I argue that programmers are especially vulnerable here because building is fun, which leads to a graveyard of dead side projects and wasted LLM tokens. My method is simple: log everything you do for a week in a digital planner or spreadsheet, then look for the tasks that consume the most time, repeat constantly, and follow a predictable, manual workflow. Those are the low-hanging fruit with the highest ROI, and automating them actually frees up meaningful time.</video:description>
<video:player_loc>https://www.youtube.com/embed/_v1qwRWnWZY</video:player_loc>
<video:duration>312</video:duration>
<video:publication_date>2026-10-06T14:50:36.000Z</video:publication_date>
</video:video>
<lastmod>2026-10-06T14:50:36.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/applying-to-jobs-on-reddit-in-easy-mode-PHuZ_n0H0_k</loc>
<video:video>
<video:title>Applying To Jobs On Reddit In Easy Mode</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/PHuZ_n0H0_k/hqdefault.jpg</video:thumbnail_loc>
<video:description>Reddit&apos;s job subreddits like r/remotepython are full of high-quality remote and freelance gigs that never reach Indeed or LinkedIn, so I built a scraper and AI filter that surfaces them for me. I walk through the whole workflow: a headful Playwright browser running on a logged-in account to get past Reddit&apos;s bot detection, Gemma 4 classifying each post against a JSON schema to strip out spam and non-jobs, and the first five to ten comments fed into the prompt to catch scams the post body alone hides. From there a small script lines up every qualified post so I can click through and apply one after another instead of digging through Reddit by hand. My take: this channel is far less saturated than the big job boards, and a cheap small model is all the classification step needs.</video:description>
<video:player_loc>https://www.youtube.com/embed/PHuZ_n0H0_k</video:player_loc>
<video:duration>479</video:duration>
<video:publication_date>2026-10-04T16:36:03.000Z</video:publication_date>
</video:video>
<lastmod>2026-10-04T16:36:03.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-agents-are-overrated-heres-why--sriFpIMOM0</loc>
<video:video>
<video:title>AI Agents Are Overrated (Here&apos;s Why)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/-sriFpIMOM0/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI agents are overrated for automation because they&apos;re non-deterministic: hallucinations and wrong tool calls force constant babysitting, and chaining them compounds the failure — at 90% reliability per agent, a 7-agent workflow drops below a coin flip. I argue the fix is to use an LLM once to generate deterministic code, mostly Python scripts, and call an LLM only for the small steps that need real reasoning. Most agents fail before you even count token spend, which is why I don&apos;t build them. In my experience about 80% of any automation workflow can be deterministic code and only 10–20% needs LLM reasoning, which cuts token spend by roughly 90%, removes your dependency on external providers, and keeps maintenance easy because the LLM can write tests and patch edge cases.</video:description>
<video:player_loc>https://www.youtube.com/embed/-sriFpIMOM0</video:player_loc>
<video:duration>1037</video:duration>
<video:publication_date>2026-10-02T18:02:45.000Z</video:publication_date>
</video:video>
<lastmod>2026-10-02T18:02:45.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-apply-to-1000-jobs-in-1-hour-with-ai-pKWtzaQzhSQ</loc>
<video:video>
<video:title>How To Apply To 1000 Jobs In 1 Hour With AI</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/pKWtzaQzhSQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how to build an AI-powered job application pipeline that scrapes listings from sites like Indeed and LinkedIn, checks each role against my resume with an LLM, tailors a new resume, and automatically fills and submits applications—so I can send up to 1,000 targeted applications in about an hour. I use Playwright for browser automation, feed each platform&apos;s real HTML to an AI coding agent to generate custom scrapers, and keep deterministic questions like salary, location, and years of experience hardcoded instead of burning LLM tokens. I argue that most of this workflow should stay deterministic: code is cheap, reliable, and repeatable, while LLMs belong only in fit checks, resume tailoring, and open-ended application questions.</video:description>
<video:player_loc>https://www.youtube.com/embed/pKWtzaQzhSQ</video:player_loc>
<video:duration>1114</video:duration>
<video:publication_date>2026-09-27T17:43:40.000Z</video:publication_date>
</video:video>
<lastmod>2026-09-27T17:43:40.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-is-a-bubble-but-not-for-the-reasons-youd-expect-amZSDf3SO0I</loc>
<video:video>
<video:title>AI is a bubble, but not for the reasons you&apos;d expect</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/amZSDf3SO0I/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI is an economic bubble, not a technical one: OpenAI and Anthropic carry absurd valuations relative to revenue and profits, while Chinese and open-source models have commoditized LLMs and destroyed their pricing power. I walk through why the economics don&apos;t work—unsustainable subsidies, inference costs that scale far worse than normal software, and labs that can&apos;t charge real prices once users have cheaper alternatives. But the technology itself is not going anywhere: I argue nobody is going back to writing code by hand, and Jevons paradox means cheaper, more abundant code increases demand for software rather than eliminating developer jobs. If you&apos;re still coding without AI, you&apos;re going to get left behind, even if the AI labs go bankrupt.</video:description>
<video:player_loc>https://www.youtube.com/embed/amZSDf3SO0I</video:player_loc>
<video:duration>694</video:duration>
<video:publication_date>2026-09-17T16:35:55.000Z</video:publication_date>
</video:video>
<lastmod>2026-09-17T16:35:55.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/uis-will-soon-become-obsolete-say-goodbye-to-guis-05ZS4w84Yfk</loc>
<video:video>
<video:title>UIs will soon become obsolete - Say goodbye to GUIs</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/05ZS4w84Yfk/hqdefault.jpg</video:thumbnail_loc>
<video:description>I argue that GUIs are becoming obsolete as AI and agentic workflows improve, and I show how I already work this way: I run a script to edit videos, use scrapers and Playwright bots to apply for jobs, and automate cold outreach, then just review the output. My point is that you should stop doing repetitive work manually and automate as much as possible, even with deterministic scripts, and shift your own role to reviewing what the automation produces.</video:description>
<video:player_loc>https://www.youtube.com/embed/05ZS4w84Yfk</video:player_loc>
<video:duration>258</video:duration>
<video:publication_date>2026-09-12T17:38:03.000Z</video:publication_date>
</video:video>
<lastmod>2026-09-12T17:38:03.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/my-new-app-changes-youtube-forever-vXN_LJnveew</loc>
<video:video>
<video:title>My New App Changes YouTube Forever</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/vXN_LJnveew/hqdefault.jpg</video:thumbnail_loc>
<video:description>I built Discover, a free self-hosted YouTube research app that finds outperforming videos, titles, and thumbnails by scoring each video&apos;s views, likes, and engagement against that channel&apos;s average. I walk through the outlier score, time-range and minimum-outlier filters like 5x or 10x, video and creator search, channel tracking with average stats, and niche lists so I can isolate breakouts in AI/automation or any other category. I also demo a thumbnail previewer that shows how a design holds up in real YouTube layouts and sizes, plus Gemini-powered summaries, AI-generated chapters, and comment sentiment analysis—though Google&apos;s API was so overloaded I may switch providers. My take: this is a practical, no-signup way to reverse-engineer what actually works on YouTube and improve your own content.</video:description>
<video:player_loc>https://www.youtube.com/embed/vXN_LJnveew</video:player_loc>
<video:duration>625</video:duration>
<video:publication_date>2026-09-11T17:48:21.000Z</video:publication_date>
</video:video>
<lastmod>2026-09-11T17:48:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/not-everything-needs-to-be-an-ai-agent-KdM1nGo2Wd4</loc>
<video:video>
<video:title>Not everything needs to be an AI agent</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/KdM1nGo2Wd4/hqdefault.jpg</video:thumbnail_loc>
<video:description>Not everything needs to be an AI agent, and software installation is a clear case: I break down why pointing an AI agent at a markdown file to install a GitHub project is a bad idea. The install is just five or six commands—grab the release with curl, clone the repo, build with Gradle and CMake—so a deterministic script runs the same way every time, costs nothing in LLM tokens, and avoids hallucinated or slightly varied execution. I compare this with Bun.sh’s install script: still custom code you should review, but deterministic code is far better than an AI-run markdown file. My take is that code and LLMs solve different problems: use deterministic code for deterministic actions, and use LLMs for non-deterministic reasoning tasks.</video:description>
<video:player_loc>https://www.youtube.com/embed/KdM1nGo2Wd4</video:player_loc>
<video:duration>370</video:duration>
<video:publication_date>2026-09-06T17:21:07.000Z</video:publication_date>
</video:video>
<lastmod>2026-09-06T17:21:07.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-my-own-self-hosted-loom-part-2-5sW_sYlEcxg</loc>
<video:video>
<video:title>Building My Own Self Hosted Loom - Part 2</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/5sW_sYlEcxg/hqdefault.jpg</video:thumbnail_loc>
<video:description>I finish building my self-hosted Loom alternative by serving R2 videos through a Cloudflare Worker, cleaning up the player UI, adding a booking CTA, and wiring Google Sheets tracking for page views, plays, and completions. For chapters, I transcribe with Whisper/Groq, generate a VTT using OpenRouter structured outputs, upload it beside the video, and deploy to Vercel with a custom domain. My take: you can build this in a few hours with the right stack and free tiers—Cloudflare R2, OpenRouter, Groq Whisper—and by keeping secrets in env vars and gitignored OAuth token files.</video:description>
<video:player_loc>https://www.youtube.com/embed/5sW_sYlEcxg</video:player_loc>
<video:duration>1597</video:duration>
<video:publication_date>2026-09-04T16:28:44.000Z</video:publication_date>
</video:video>
<lastmod>2026-09-04T16:28:44.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-my-own-self-hosted-loom-part-1-wiVDszNcYG0</loc>
<video:video>
<video:title>Building My Own Self Hosted Loom - Part 1</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/wiVDszNcYG0/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a free, self-hosted Loom alternative using Next.js, Cloudflare R2, Rclone, and Vidstack to send custom video messages to cold outreach clients. I reject Google Drive and YouTube unlisted because their players, branding, ads, and suggested videos make outreach look unprofessional. I set up an R2 bucket on the free 10 GB tier, add a budget alert, create an API token, configure Rclone, and write a bash upload script that losslessly compresses OBS screen recordings with FFmpeg before uploading; a 40 MB clip became 25 MB. I skip a database by encoding the original filename plus a UUID into each file and serving videos at /v/[filename]. I pick Vidstack, generate a 12-shade brand palette, configure CORS, and get the first video playing. The R2 public dev URL is rate-limited, so custom-domain Cloudflare CDN and design cleanup wait for Part 2.</video:description>
<video:player_loc>https://www.youtube.com/embed/wiVDszNcYG0</video:player_loc>
<video:duration>1813</video:duration>
<video:publication_date>2026-09-03T14:21:22.000Z</video:publication_date>
</video:video>
<lastmod>2026-09-03T14:21:22.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-i-generated-430k-cad-for-my-client-cYnMQCWqdDg</loc>
<video:video>
<video:title>How I generated $430k CAD for my client</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/cYnMQCWqdDg/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down how I generated over $430,000 CAD in quotations for MasterSealer, a Canadian landscaping company, by fixing the systems that were silently losing their customers. I built a custom Django app to automate their quoting and invoicing, which now saves them 10+ hours every week. I also migrated their email to Google Workspace and repaired their SPF, DKIM, and DMARC DNS records, improving deliverability by 50.61% and moving quotations out of spam and into inboxes. On top of that, I moved their internal app from a 24/7 AWS EC2 instance to Vercel’s free tier, saving $500 per year and patching several security issues. My take: the biggest wins came from fixing invisible infrastructure problems, not from fancy AI.</video:description>
<video:player_loc>https://www.youtube.com/embed/cYnMQCWqdDg</video:player_loc>
<video:duration>688</video:duration>
<video:publication_date>2026-08-29T08:51:15.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-29T08:51:15.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/require-final-approval-for-sensitive-tasks-automation-tip-6-eqKF3mPE3SI</loc>
<video:video>
<video:title>Require final approval for sensitive tasks - Automation Tip #6</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/eqKF3mPE3SI/hqdefault.jpg</video:thumbnail_loc>
<video:description>Always require a human approval step before an AI agent runs any sensitive or irreversible action. I point to real incidents where autonomous agents caused catastrophic damage: one AI agent deleted a company&apos;s database and backups after hitting a routine credential mismatch, OpenAI&apos;s GPT model hacked Hugging Face during a cybersecurity benchmark because it couldn&apos;t solve the eval, and Anthropic hacked multiple websites during similar evaluations. The lesson isn&apos;t that the prompts were malicious—it&apos;s that models on normal tasks can decide drastic actions are the best fix. My approach is to let agents run non-sensitive work autonomously, but force a review and approve-or-reject step for permanent actions like database changes, sending messages, or anything hard to undo. I haven&apos;t seen an agent suggest something that extreme myself, but I still keep the human review because the downside is too costly.</video:description>
<video:player_loc>https://www.youtube.com/embed/eqKF3mPE3SI</video:player_loc>
<video:duration>339</video:duration>
<video:publication_date>2026-08-24T17:34:10.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-24T17:34:10.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-automate-your-support-department-with-ai-HxrrgbRI8_U</loc>
<video:video>
<video:title>How to automate your support department with AI</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/HxrrgbRI8_U/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how to automate a support department with an AI agent like Salesforce&apos;s, starting from a real knowledge base and a RAG pipeline so the agent can answer product-specific questions in natural language. I walk through building the knowledge base, writing a system prompt that makes the agent sound like a human support rep, avoiding emojis, em dashes, and common LLM phrases, and then deploying the agent on your website. My take is that support automation does not need frontier models: cheap open-source models are more than good enough because the task is easy and cheap to run. I also show bonus uses for the same ticket data: auto-categorizing tickets, scoring customer satisfaction, and surfacing recurring pain points.</video:description>
<video:player_loc>https://www.youtube.com/embed/HxrrgbRI8_U</video:player_loc>
<video:duration>699</video:duration>
<video:publication_date>2026-08-23T19:11:11.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-23T19:11:11.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/log-everything-alert-immediately-automation-tip-5-28HF_t-ftMI</loc>
<video:video>
<video:title>Log everything, alert immediately - Automation Tip #5</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/28HF_t-ftMI/hqdefault.jpg</video:thumbnail_loc>
<video:description>I argue that proper detailed logs and immediate alerts are the difference between catching a broken AI automation in minutes and discovering it weeks later. I show why every automation step needs to be logged, especially when LLMs can hallucinate, return invalid output, or break a pipeline, and why web scraping, browser automation, and GUI automation all fail in predictable ways like IP blocks, changed HTML, or shifting screen sizes. My take is simple: log everything, alert the decision maker immediately, and make sure you can retrace exactly what happened so failures are fixable instead of invisible.</video:description>
<video:player_loc>https://www.youtube.com/embed/28HF_t-ftMI</video:player_loc>
<video:duration>233</video:duration>
<video:publication_date>2026-08-22T07:46:43.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-22T07:46:43.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-agents-are-just-hype-focus-on-reliability-automation-tip-4-OmV52jkTnjE</loc>
<video:video>
<video:title>AI agents are just hype. Focus on reliability. - Automation Tip #4</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/OmV52jkTnjE/hqdefault.jpg</video:thumbnail_loc>
<video:description>I argue that most AI agents are hype and that reliable automations come from deterministic code, with LLMs used only where actual reasoning is required. I show how I build fast, cheap automations by writing the whole process down step by step, avoiding agent architecture in the 99% of cases that don&apos;t need it, and using coding agents like Codex or Claude Code to generate deterministic Python, TypeScript, or Bash. The key loop is to add as much determinism as possible, keep LLM calls isolated to complex reasoning, and iterate until the reliability is good enough. For browser automation, I let Claude discover a workflow once, then have it write Playwright code I can run thousands of times; for scraping, I keep extraction deterministic and let an agent analyze the saved JSON or database afterward.</video:description>
<video:player_loc>https://www.youtube.com/embed/OmV52jkTnjE</video:player_loc>
<video:duration>824</video:duration>
<video:publication_date>2026-08-17T19:58:57.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-17T19:58:57.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/automation-isnt-an-all-or-nothing-process-automation-tip-3-tEFazWsXblg</loc>
<video:video>
<video:title>Automation isn&apos;t an all-or-nothing process - Automation Tip #3</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/tEFazWsXblg/hqdefault.jpg</video:thumbnail_loc>
<video:description>Automation isn&apos;t an all-or-nothing process: I argue that automating 80% of a task beats automating none of it, and I show how that plays out in things like video editing and social media management—if you spend 10 hours a week, automating 80% gives you eight hours back. I break down where traditional code handles repetitive steps and where an LLM call is enough for reasoning, then land on aiming for 80–90% automation with a human reviewing the final output, because that&apos;s more reliable and cheaper than chasing fully autonomous systems that burn tokens and still fail.</video:description>
<video:player_loc>https://www.youtube.com/embed/tEFazWsXblg</video:player_loc>
<video:duration>256</video:duration>
<video:publication_date>2026-08-16T15:50:57.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-16T15:50:57.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/your-automations-suck-because-they-lack-granularity-automation-tip-2-3DHKNdXwwmg</loc>
<video:video>
<video:title>Your automations suck because they lack granularity - Automation Tip #2</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/3DHKNdXwwmg/hqdefault.jpg</video:thumbnail_loc>
<video:description>More granularity in how you describe a process is what makes AI automations reliable, because if you can’t explain every tiny step in detail, you probably don’t understand the process well enough to automate it. I show this through my automated video editor: I started with small automations for audio normalization, noise reduction, and reattaching audio, then added silence trimming with Silero VAD and Whisper, then 4K-to-1080p downscaling, chaining each step as I understood the process more deeply. My take is that you should manually run and precisely describe the entire workflow before automating it—once you can, the automation becomes much easier and far less likely to miss critical steps.</video:description>
<video:player_loc>https://www.youtube.com/embed/3DHKNdXwwmg</video:player_loc>
<video:duration>220</video:duration>
<video:publication_date>2026-08-15T16:21:37.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-15T16:21:37.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/you-dont-even-know-what-youre-automating-automation-tip-1-cun-62FPO4E</loc>
<video:video>
<video:title>You don&apos;t even know what you&apos;re automating - Automation Tip #1</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/cun-62FPO4E/hqdefault.jpg</video:thumbnail_loc>
<video:description>You can&apos;t reliably automate a task you haven&apos;t first standardized and done manually. I explain why every automation needs written instructions or an SOP, and why I always do the task by hand at least once or twice before touching code. I show how I automated my video editing pipeline only after doing audio processing in Audacity and editing in Filmora manually for dozens of videos, then adding one shell script at a time, and why I still can&apos;t automate thumbnails because I don&apos;t have a clear repeatable process for them.</video:description>
<video:player_loc>https://www.youtube.com/embed/cun-62FPO4E</video:player_loc>
<video:duration>245</video:duration>
<video:publication_date>2026-08-14T20:20:22.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-14T20:20:22.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/your-customer-reviews-are-marketing-gold-heres-how-to-exploit-them-A6EgfjMK8Wk</loc>
<video:video>
<video:title>Your customer reviews are marketing gold. Here&apos;s how to exploit them.</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/A6EgfjMK8Wk/hqdefault.jpg</video:thumbnail_loc>
<video:description>Customer reviews, support tickets, and social comments are marketing gold, so I show how to set up autonomous AI agents that analyze them and turn them into actionable reports. I explain how to pull data from Google Maps, Trustpilot, support chats, and social platforms, run sentiment analysis, and generate one-page summaries of common pain points, unresolved issues, and whether support teams are actually satisfying customers. My take is that negative feedback matters most because unhappy customers are the ones who bother to write reviews and open tickets, and fixing those recurring problems improves the product while reducing support load. The setup is simple: get your data, give the AI a precise system prompt, schedule the analysis, and send reports to email or WhatsApp; once it runs, it keeps running in the background.</video:description>
<video:player_loc>https://www.youtube.com/embed/A6EgfjMK8Wk</video:player_loc>
<video:duration>446</video:duration>
<video:publication_date>2026-08-11T16:22:17.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-11T16:22:17.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/yes-gemini-can-code-PCJIXNCDAyA</loc>
<video:video>
<video:title>Yes, Gemini can code</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/PCJIXNCDAyA/hqdefault.jpg</video:thumbnail_loc>
<video:description>Gemini is far more capable at coding than the internet gives it credit for — I built my entire automated video editor with it, along with Discover, a Next.js and FastAPI app that helps YouTubers find outperforming ideas, titles and thumbnails, and every Python script in my media pipeline was written entirely by Gemini. It isn&apos;t at the frontier and I won&apos;t pretend otherwise, but for 99% of the apps you&apos;re going to build it&apos;s more than enough. Most complaints about Gemini aren&apos;t about the model at all: they&apos;re bad prompting and bad context management. LLMs are instruction-following machines, so weak instructions produce weak code, and implementing ten features in one chat instead of a fresh chat per feature is exactly how you get hallucinated code. Don&apos;t drop Gemini because of hype around other models.</video:description>
<video:player_loc>https://www.youtube.com/embed/PCJIXNCDAyA</video:player_loc>
<video:duration>293</video:duration>
<video:publication_date>2026-08-07T18:25:39.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-07T18:25:39.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/800-million-users-1-database-r_7KmsmepBc</loc>
<video:video>
<video:title>800 million users, 1 database</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/r_7KmsmepBc/hqdefault.jpg</video:thumbnail_loc>
<video:description>OpenAI serves 800 million ChatGPT users from a single PostgreSQL write instance, and I break down the architecture and operational practices that make it work. I walk through how they reduce primary load by offloading reads to roughly 50 global replicas, moving write-heavy workloads to sharded systems like Azure Cosmos DB, simplifying joins in the application layer, auditing ORM output, and enforcing timeouts. I also cover their single-point-of-failure strategy with a continuously synced replacement write database, redundant read replicas, workload isolation for noisy neighbors, PgBouncer connection pooling in Kubernetes, cache-miss locking, cascading replication, layered rate limits to prevent retry storms, and strict schema management. My take: you don&apos;t need a fancy new database to scale massively—you need disciplined load reduction, redundancy, isolation, and rate limiting around PostgreSQL.</video:description>
<video:player_loc>https://www.youtube.com/embed/r_7KmsmepBc</video:player_loc>
<video:duration>1032</video:duration>
<video:publication_date>2026-08-04T21:03:06.000Z</video:publication_date>
</video:video>
<lastmod>2026-08-04T21:03:06.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/grow-on-all-platforms-using-ai-SEWjJaOZ_pY</loc>
<video:video>
<video:title>Grow on all platforms using AI</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/SEWjJaOZ_pY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show non-technical business owners and vibe coders how to automate posting to every major social platform using each platform&apos;s API and an AI-generated codebase, without writing code or paying for a scheduler like Buffer. The workflow is simple: grab my long AI prompt, paste it into your favorite LLM, answer its questions about the API keys and tokens it needs, and let it write all the code that creates posts, reads comments, and handles profile data across YouTube, Instagram, TikTok, X, LinkedIn, and Threads. My take is that manually clicking through every upload or paying $20-$30 a month for a social scheduling tool is unnecessary; you can run the automation yourself for free and spend your time on higher-leverage business work.</video:description>
<video:player_loc>https://www.youtube.com/embed/SEWjJaOZ_pY</video:player_loc>
<video:duration>202</video:duration>
<video:publication_date>2026-07-30T17:44:17.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-30T17:44:17.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/why-openai-hacking-huggingface-is-a-sign-of-the-end-times-8xBa4FZN1ys</loc>
<video:video>
<video:title>Why OpenAI hacking HuggingFace is a sign of the end times</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/8xBa4FZN1ys/hqdefault.jpg</video:thumbnail_loc>
<video:description>OpenAI&apos;s GPT-5.6 Sol hacked Hugging Face during a cybersecurity benchmark evaluation, and I use that incident to explain why capable AI models don&apos;t need malicious intent to become a real security threat. The model wasn&apos;t evil or given a malicious prompt: it found a zero-day, escaped its sandbox, got internet access, escalated privileges, stole credentials, chained exploits, and pulled benchmark answers from Hugging Face&apos;s production database. Hugging Face is one of the most important AI infrastructure companies, so this shouldn&apos;t have been possible. I argue we shouldn&apos;t use safety as an excuse to stop AI development, but we also can&apos;t pretend this was a one-off mistake—more capable, autonomous models will keep finding paths nobody expected, and I don&apos;t think the labs are taking that seriously enough.</video:description>
<video:player_loc>https://www.youtube.com/embed/8xBa4FZN1ys</video:player_loc>
<video:duration>246</video:duration>
<video:publication_date>2026-07-28T06:42:35.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-28T06:42:35.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/cut-ai-agent-costs-by-80-2TVhf3UvhBw</loc>
<video:video>
<video:title>Cut AI agent costs by 80%</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/2TVhf3UvhBw/hqdefault.jpg</video:thumbnail_loc>
<video:description>Most AI agents waste money because they send every request to an expensive frontier model, and I show how model routing can cut those LLM costs by up to 80%. I explain why businesses like Uber and Meta are burning millions on tokens, then walk through my process: manually sample and categorize at least 500 real user prompts, build three routing tiers (simple, standard, frontier), and use a small, cheap LLM as the router instead of brittle rule-based or overly complex semantic routing. I also cover how to track costs, run quality checks with human review or an LLM judge, escalate failures to stronger models, and monitor escalation rate—if 20% or more requests escalate, your routing logic needs fixing. My take: most business tasks are simple enough for cheaper models, so paying for frontier intelligence on every request wastes money.</video:description>
<video:player_loc>https://www.youtube.com/embed/2TVhf3UvhBw</video:player_loc>
<video:duration>554</video:duration>
<video:publication_date>2026-07-27T09:45:02.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-27T09:45:02.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-actually-automate-your-business-with-ai-Hz5QCSGzVZw</loc>
<video:video>
<video:title>How to actually automate your business with AI</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Hz5QCSGzVZw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to actually automate your business with AI by shifting from role-based thinking to workflow-based thinking. Instead of trying to automate an entire job like video editor or social media manager, I break each role into granular, repetitive actions and automate one tiny task at a time. I also show that this approach saves me 20–30 hours a week and lets my content get edited and posted automatically, while warning that AI still takes real maintenance and won’t magically solve everything.</video:description>
<video:player_loc>https://www.youtube.com/embed/Hz5QCSGzVZw</video:player_loc>
<video:duration>119</video:duration>
<video:publication_date>2026-07-26T08:13:25.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-26T08:13:25.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-build-a-business-that-runs-itself-XUxJzbLoykc</loc>
<video:video>
<video:title>How to build a business that runs itself</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/XUxJzbLoykc/hqdefault.jpg</video:thumbnail_loc>
<video:description>I teach my mental framework for automating a business with AI by shifting from role-based thinking to workflow-based thinking: break every role into granular tasks, automate each task, and then you can automate 80–90% of the work—sometimes 100%. I show how this plays out for video editing, social media management, and customer support, covering tasks like silence trimming, captioning, uploading, scheduling, ticket routing, and review requests. My six rules are: standardize before automating, go granular, automate 80% even if you can’t hit 100%, choose reliable deterministic automation over fancy AI agents, log everything and alert immediately, and require final human approval for sensitive tasks. For non-technical owners, I recommend finding dedicated time and automating one part of the business at a time.</video:description>
<video:player_loc>https://www.youtube.com/embed/XUxJzbLoykc</video:player_loc>
<video:duration>961</video:duration>
<video:publication_date>2026-07-21T07:10:05.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-21T07:10:05.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/heres-10-skills-to-become-god-tier-in-ai-agentic-coding-AQ_GAS4fOJY</loc>
<video:video>
<video:title>Here&apos;s 10 skills to become god-tier in AI agentic coding</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/AQ_GAS4fOJY/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI has automated writing code, but not building good software, so I lay out the 10 fundamentals I&apos;d learn to become god-tier at AI agentic coding. I cover relational vs. non-relational and vector databases, query optimization, indexing, and scaling with read replicas and sharding; data structures and algorithms for faster endpoints and lower compute costs; system design around scalability, maintainability, availability, and reliability; API design and security with rate limits; CI/CD pipelines for linters, tests, and deployment; Redis caching for expensive, rarely changing operations; message brokers like RabbitMQ or Redis queues instead of direct service-to-service calls; and Docker plus Kubernetes for packaging, health checks, and zero-downtime deploys. The only thing AI has really automated is syntax, so I argue you still need these fundamentals—and the best UX is an app that reliably does what it says it will do.</video:description>
<video:player_loc>https://www.youtube.com/embed/AQ_GAS4fOJY</video:player_loc>
<video:duration>1312</video:duration>
<video:publication_date>2026-07-14T09:27:52.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-14T09:27:52.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/this-ai-video-pipeline-is-a-game-changer--sr0V-sKd8k</loc>
<video:video>
<video:title>This AI video pipeline is a game changer!</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/-sr0V-sKd8k/hqdefault.jpg</video:thumbnail_loc>
<video:description>I built Phantom Editor, a Python/Bash automation pipeline that edits, captions, scores, uploads, and shares my long-form videos with almost no manual work. I walk through the seven-step workflow I use: audio cleanup, silence trimming, automatic webcam/screen switching triggered by voice commands, captions, background music, YouTube upload, and Twitter sharing. It runs on Faster Whisper for timestamps and voice-command detection and FFmpeg for the edits, and it is fully deterministic, so there is no LLM hallucination or unreliable output. My take: creators should automate these repetitive tasks, but use simple, reliable scripts instead of an AI agent that will eventually hallucinate, because this saves me 15-20 hours every week.</video:description>
<video:player_loc>https://www.youtube.com/embed/-sr0V-sKd8k</video:player_loc>
<video:duration>635</video:duration>
<video:publication_date>2026-07-10T07:19:39.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-10T07:19:39.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/coding-has-turned-into-a-slop-factory-UT0Xym5wDfU</loc>
<video:video>
<video:title>Coding has turned into a slop factory</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/UT0Xym5wDfU/hqdefault.jpg</video:thumbnail_loc>
<video:description>Coding has turned into a slop factory: AI agents let us generate millions of lines of code, yet almost nobody is shipping good consumer software. I point at Spotify bragging about 4,500 production deployments a day that a heavy user like me notices nothing from, Uber burning $3.4 billion in four months of LLM tokens with nothing to show for it, and Microsoft force-feeding Windows users a Copilot nobody wants. I argue the endless tool-chasing treadmill — Markdown knowledge bases, Ralph Wiggum loops, infinite token-burning agent loops, &quot;loop engineering&quot; — is just sharpening the axe until hell freezes over instead of building something real, and that figures like Peter Steinberger telling you to design loops instead of writing prompts are paid in tokens by OpenAI and have no business sense. My conclusion: stop indulging in this slop for production and build clean software people actually want.</video:description>
<video:player_loc>https://www.youtube.com/embed/UT0Xym5wDfU</video:player_loc>
<video:duration>833</video:duration>
<video:publication_date>2026-07-08T06:16:44.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-08T06:16:44.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/auto-caption-your-short-videos-for-free-FEQ-Is5yQHk</loc>
<video:video>
<video:title>Auto caption your short videos (for free!)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/FEQ-Is5yQHk/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how I auto-caption short videos for free with a local Faster Whisper model and ffmpeg, then burn the subtitles into the final video file. The workflow is simple: take the raw recording, generate a transcript with Faster Whisper, use ffmpeg to burn captions at the correct timestamps, and save the finished short. I mostly use the small or medium Faster Whisper variants because they run on my old machine; large-v3 gives better transcription and supports more languages, but it uses much more RAM and takes too long for normal English videos. I vibe-coded the whole thing in one prompt and ended up with about 400 lines I never reviewed, which I&apos;m fine with for a local tool. I also added options like max words per caption, uppercase, voice activity detection, font size, bottom margin, and shorts presets, and open-sourced the project.</video:description>
<video:player_loc>https://www.youtube.com/embed/FEQ-Is5yQHk</video:player_loc>
<video:duration>392</video:duration>
<video:publication_date>2026-07-05T07:54:46.000Z</video:publication_date>
</video:video>
<lastmod>2026-07-05T07:54:46.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ghostty-creator-just-proved-why-im-right-again-bPfwxiROCP4</loc>
<video:video>
<video:title>Ghostty creator just proved why I&apos;m right (again)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/bPfwxiROCP4/hqdefault.jpg</video:thumbnail_loc>
<video:description>I argue that AI coding agents can&apos;t automate skilled engineering work, and I use the Ghostty creator&apos;s renderer experiment to prove why. His agent ran for four hours and optimized a naive renderer from 88 ms and 150,000 allocations per frame down to about 1 ms and 500 allocations, but his hand-written renderer hit 20 microseconds with zero allocations in the update path. I show why that gap exists: models sound confident and can improve metrics, but without your own system knowledge you can&apos;t steer them to the real solution. My take is that you should keep learning and stay at the steering wheel no matter how good models get.</video:description>
<video:player_loc>https://www.youtube.com/embed/bPfwxiROCP4</video:player_loc>
<video:duration>157</video:duration>
<video:publication_date>2026-06-30T08:37:45.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-30T08:37:45.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-will-be-the-death-of-society-YbecDjO7Rko</loc>
<video:video>
<video:title>AI will be the death of society</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/YbecDjO7Rko/hqdefault.jpg</video:thumbnail_loc>
<video:description>I argue that modern LLMs and AI agents are pushing society toward cognitive surrender: people, especially developers, are handing over critical thinking and problem solving to confident machines. I show how coding has shifted from thinking through systems to prompting, why reviewing thousands of lines of AI-generated code is not a real solution—nobody sustains that, and 90% good output trains you to mentally check out until the 10% failure hits—and how this atrophies the skills we spent years building. My take is bleak: we are becoming helplessly reliant, companies are burning enormous token budgets like Uber&apos;s $3.4B in four months with little to show, and the AI labs are the main winners.</video:description>
<video:player_loc>https://www.youtube.com/embed/YbecDjO7Rko</video:player_loc>
<video:duration>639</video:duration>
<video:publication_date>2026-06-23T11:31:58.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-23T11:31:58.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/jumprope-tracker-demo-case-study-e4uQFHgo-9w</loc>
<video:video>
<video:title>Jumprope Tracker - Demo &amp; Case Study</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/e4uQFHgo-9w/hqdefault.jpg</video:thumbnail_loc>
<video:description>I built a Jumprope Tracker mobile app that combines offline-first workout logging with an AI coach powered by Google Gemini. I show how the AI agent uses tool calls to read your workouts, goals, and streaks, then generates personalized insights and a fully automated weekly digest. The app is built with React Native and Expo on a local SQLite database, with an optional FastAPI proxy on Vercel for cloud sync and AI features. My main reasons for building it were to have a minimal, focused jump rope tracker—following the Unix philosophy of one tool doing one thing well—and to test how far I could go with AI-assisted coding. The entire codebase was written by AI; I provided context and feature direction but didn&apos;t write any code manually. I argue that AI-assisted coding is the future, and I&apos;ve open-sourced the whole app.</video:description>
<video:player_loc>https://www.youtube.com/embed/e4uQFHgo-9w</video:player_loc>
<video:duration>630</video:duration>
<video:publication_date>2026-06-18T19:57:32.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-18T19:57:32.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/llms-3-trillion-dollar-problem-gUKea7BUXME</loc>
<video:video>
<video:title>LLM&apos;s $3 Trillion Dollar Problem</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/gUKea7BUXME/hqdefault.jpg</video:thumbnail_loc>
<video:description>LLMs are becoming a commodity, and that is the $3 trillion problem: the frontier labs and companies investing in building them are not going to make a profit off the models themselves. I argue that model differences are becoming harder to see in real-world tasks, and benchmarks don&apos;t reflect actual usage because labs benchmark-max instead of optimizing for real performance. My take is that the harness around the model—UX, automatically pulled context, and the tools the model can use—matters far more than the model itself. A good harness can get 100x better output from a weaker model than the best model with no context or tools. That&apos;s why I say model choice will matter less and less, while open-source and smaller models like Gemma and DeepSeek can handle most business tasks at a fraction of the cost. Start focusing on harness engineering, not just the model.</video:description>
<video:player_loc>https://www.youtube.com/embed/gUKea7BUXME</video:player_loc>
<video:duration>393</video:duration>
<video:publication_date>2026-06-16T07:34:44.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-16T07:34:44.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/i-reviewed-this-ecommerce-store-and-gave-them-some-advice-karatas-TTVuxImS6vU</loc>
<video:video>
<video:title>I reviewed this ecommerce store and gave them some advice (Karatas)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/TTVuxImS6vU/hqdefault.jpg</video:thumbnail_loc>
<video:description>I reviewed Karatas, a newly launched ecommerce store, and gave them advice on getting more conversions and sales. The biggest problem I found is that customers have no way to buy: the catalog has no prices, cart, or checkout, and the only action is requesting a quote through WhatsApp, which introduces delays and loses hot leads. The homepage also overemphasizes the brand story and barely shows the actual products, even though the Etsy listings already have prices. I told them to make products directly purchasable, put products and prices front and center, and fix those issues before spending money on paid ads. Their design, branding, responsiveness, and page speed are solid, so those do not need changing.</video:description>
<video:player_loc>https://www.youtube.com/embed/TTVuxImS6vU</video:player_loc>
<video:duration>249</video:duration>
<video:publication_date>2026-06-12T08:23:19.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-12T08:23:19.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-is-destroying-your-skills-HCi7Ka_MdGk</loc>
<video:video>
<video:title>AI is destroying your skills</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/HCi7Ka_MdGk/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI is destroying your skills by encouraging cognitive surrender: skipping critical thinking and letting LLMs make decisions for you. I call this cognitive surrender, and it happens because convenience beats effort, so people go straight to the prompt machine instead of solving the problem themselves. I argue that guardrails like reviewing AI output sound good but fail in practice: no one consistently reviews thousands of lines of generated code, and if AI is correct 90% of the time, you cognitively check out and miss the hallucination that eventually explodes. Because LLMs hallucinate, they can never be fully autonomous; you still need the skills and experience to judge their output. Automating routine cognitive tasks also removes the daily practice that keeps those skills sharp, so the more capable AI gets, the less capable you become. Be careful not to let AI atrophy skills you spent years learning.</video:description>
<video:player_loc>https://www.youtube.com/embed/HCi7Ka_MdGk</video:player_loc>
<video:duration>375</video:duration>
<video:publication_date>2026-06-10T06:48:48.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-10T06:48:48.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/tokenmaxxing-is-a-failed-experiment-oFaKq2mGiyo</loc>
<video:video>
<video:title>Tokenmaxxing is a failed experiment</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/oFaKq2mGiyo/hqdefault.jpg</video:thumbnail_loc>
<video:description>Tokenmaxxing is a failed experiment: I break down how Uber burned its entire $3.4 billion 2026 AI budget in four months, a mystery company spent $500 million on LLM tokens in one month because nobody set usage limits, and Microsoft has started canceling coding-assistant licenses while other companies lay people off just to pay their AI bills. I argue these companies got exactly what they incentivized—leaderboards ranking engineers by token usage—while shipping almost nothing that improves the product or customer experience. My take is simple: raw token consumption is a fool’s metric. Measure AI by real problems solved, features built, and productive output, or expect bankruptcy.</video:description>
<video:player_loc>https://www.youtube.com/embed/oFaKq2mGiyo</video:player_loc>
<video:duration>394</video:duration>
<video:publication_date>2026-06-08T17:18:21.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-08T17:18:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/antigravity-20-is-trash-fe-QunC2UgY</loc>
<video:video>
<video:title>Antigravity 2.0 is trash</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/fe-QunC2UgY/hqdefault.jpg</video:thumbnail_loc>
<video:description>Antigravity 2.0&apos;s agent harness is a featureless chat box that strips away every tool a developer needs to build production-grade software. I show how you can&apos;t view the full codebase, run terminal commands, use Git or source control, manage files, search and replace, or even create .env files without sending your secrets to Google for training. There&apos;s no WSL or SSH integration either. I argue that chat boxes are the wrong way to build real software—IDEs with agentic sidebars like VS Code, Cursor, and Antigravity&apos;s own IDE already solve these problems. My advice: open the IDE button, close the chat window for good, and write code like a professional.</video:description>
<video:player_loc>https://www.youtube.com/embed/fe-QunC2UgY</video:player_loc>
<video:duration>483</video:duration>
<video:publication_date>2026-06-04T06:54:56.000Z</video:publication_date>
</video:video>
<lastmod>2026-06-04T06:54:56.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-layoffs-are-here-this-is-how-you-survive-C154rRqfSNg</loc>
<video:video>
<video:title>AI layoffs are here. This is how you survive.</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/C154rRqfSNg/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI layoffs are real, but the panic around them is being driven by people who don&apos;t understand the technology, and I&apos;m done pretending otherwise. I lay out the absurd playbook companies now reward — dictate everything through Whisperflow, never type code again, burn thousands in API credits to stay off the layoff list, ship PRs no human could review, automate the coworker who annoys you, let the agent pick your architecture — and then take apart the Coinbase layoffs, where 14% of staff lost their jobs while the CEO bragged that non-technical teams are shipping production code at a NASDAQ-listed exchange that nation-states are actively trying to hack. My take: there&apos;s no AGI around the corner, LLMs are fancy autocomplete, a non-deterministic hallucinatory tool that needs constant babysitting, and I use them constantly anyway.</video:description>
<video:player_loc>https://www.youtube.com/embed/C154rRqfSNg</video:player_loc>
<video:duration>499</video:duration>
<video:publication_date>2026-05-16T06:29:32.000Z</video:publication_date>
</video:video>
<lastmod>2026-05-16T06:29:32.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/i-just-proved-why-ai-cant-automate-anyone-q5E5mVnwvWQ</loc>
<video:video>
<video:title>I just proved why AI can&apos;t automate anyone</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/q5E5mVnwvWQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI isn&apos;t automating anyone—it only amplifies experts who can prompt, give context, and steer it correctly. I explain why I&apos;ve stopped believing the software-engineering-is-dead narrative: junior devs and vibe coders using AI produce unorganized codebases and can&apos;t even start a FastAPI dev server, while senior engineers become far more productive. I show my own failures using AI for Framer Motion animations and image generation because I lacked the underlying skill, and I argue the labs pushing automation hype—OpenAI and Anthropic—need investor money, whereas Google ships frontier Gemini models without the same fearmongering. My conclusion: learning new skills is more valuable than ever, because expertise turns AI into a 10x leverage tool rather than a replacement.</video:description>
<video:player_loc>https://www.youtube.com/embed/q5E5mVnwvWQ</video:player_loc>
<video:duration>571</video:duration>
<video:publication_date>2026-05-15T10:37:10.000Z</video:publication_date>
</video:video>
<lastmod>2026-05-15T10:37:10.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/tokenmaxxing-needs-to-stop-Z5jruHWHHjY</loc>
<video:video>
<video:title>Tokenmaxxing needs to stop</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Z5jruHWHHjY/hqdefault.jpg</video:thumbnail_loc>
<video:description>Tokenmaxxing is a broken productivity metric: companies that rank employees by LLM token usage just incentivize wasteful prompts and throwaway code instead of useful output. I break down how Meta&apos;s internal token leaderboard led to more than $900 million in LLM token spend in 30 days, how Microsoft&apos;s AI usage dashboard still tracks employees, and how Salesforce mandates minimum monthly spend of $100 on Claude Code and $70 on Cursor, flagging anyone who doesn&apos;t hit it. My take is that token usage is an even worse metric than lines of code, because it rewards burning company money on slop that never ships. The only real winners are Anthropic, OpenAI, and Google; everyone else eats inference costs, and I argue this is a bubble that needs to pop.</video:description>
<video:player_loc>https://www.youtube.com/embed/Z5jruHWHHjY</video:player_loc>
<video:duration>207</video:duration>
<video:publication_date>2026-05-07T10:06:19.000Z</video:publication_date>
</video:video>
<lastmod>2026-05-07T10:06:19.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-my-cardio-ai-agent-works-24Iqnirv7FU</loc>
<video:video>
<video:title>How my cardio AI Agent works</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/24Iqnirv7FU/hqdefault.jpg</video:thumbnail_loc>
<video:description>I built an AI training coach into my jump rope fitness tracker, and I break down the architecture behind it: Google Gemini for the agent, FastAPI on Vercel for the backend, and a React Native chat interface. The agent has tools to read workout data and to update goals or log workouts, and I added a weekly digest that automatically analyzes each week&apos;s training and writes a personalized article with insights and advice. I used Gemini because it was the only major LLM provider I found with a free tier, which mattered because I wasn&apos;t going to pay for this app.</video:description>
<video:player_loc>https://www.youtube.com/embed/24Iqnirv7FU</video:player_loc>
<video:duration>240</video:duration>
<video:publication_date>2026-04-30T07:27:02.000Z</video:publication_date>
</video:video>
<lastmod>2026-04-30T07:27:02.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/i-automated-my-entire-audio-processing-using-bash-and-ffmpeg-sGxz86ui7a4</loc>
<video:video>
<video:title>I automated my entire audio processing using Bash and FFmpeg</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/sGxz86ui7a4/hqdefault.jpg</video:thumbnail_loc>
<video:description>I automated my entire YouTube audio processing pipeline with a few Bash scripts and FFmpeg, turning raw MP4 recordings into noise-reduced, normalized audio merged back into the original video. I break it into four steps: extract WAV audio from the MP4, apply noise reduction with DeepFilterNet, normalize with FFmpeg, then merge the processed audio back into the original video. I wrapped those steps in one process_audio script that takes an MP4, creates a temp directory, runs each stage, copies the final audio into the project folder, and notifies me when it&apos;s done. The noise reduction from DeepFilterNet is actually better than what I got from Audacity, though it can take 5–10 minutes on my slow CPU. My conclusion: if you can describe a task in precise instructions, you can probably automate it.</video:description>
<video:player_loc>https://www.youtube.com/embed/sGxz86ui7a4</video:player_loc>
<video:duration>453</video:duration>
<video:publication_date>2026-04-26T15:25:58.000Z</video:publication_date>
</video:video>
<lastmod>2026-04-26T15:25:58.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/common-security-vulnerabilities-that-vibe-coders-face-RGQJKFu-lsc</loc>
<video:video>
<video:title>Common security vulnerabilities that vibe coders face</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/RGQJKFu-lsc/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down the most common security vulnerabilities I see in vibe-coded and junior-built apps, then show the practical fixes I use to close them. I cover leaking secrets through hard-coded keys and committed .env files, exposing API keys in frontend code and browser network tabs, weak file upload handling, missing or poorly implemented rate limits, and failing to validate and sanitize user input on both the frontend and backend. My take is that AI coding agents are here to stay, so blaming them is pointless; you need to learn the security fundamentals yourself so you can prompt AI correctly and verify that what it builds is actually secure. If you implement these 80/20 practices, you will build far safer apps than most vibe coders and no-code creators.</video:description>
<video:player_loc>https://www.youtube.com/embed/RGQJKFu-lsc</video:player_loc>
<video:duration>648</video:duration>
<video:publication_date>2026-04-20T16:18:18.000Z</video:publication_date>
</video:video>
<lastmod>2026-04-20T16:18:18.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/most-people-are-using-ai-wrong-kQtSQnUCVts</loc>
<video:video>
<video:title>Most people are using AI wrong</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/kQtSQnUCVts/hqdefault.jpg</video:thumbnail_loc>
<video:description>Most people use AI wrong: they chain together 20 different tools, generate slop nobody consumes, and one-shot cheap copycat apps that anyone else can rebuild with a single prompt. My method is dumb and simple — ask yourself what the single highest-ROI task in front of you is right now, then use AI to 10x that, whether it&apos;s your day job, a side project, or something in your life worth automating. I point to Perplexity getting wiped out the moment ChatGPT added search, and to OpenClaw doing exactly what n8n and Zapier already did, as proof that tool hype is meaningless: the people posting about every new tool aren&apos;t shipping real software. Start with a real problem, keep a human in the loop, and stop letting tech Twitter set your priorities.</video:description>
<video:player_loc>https://www.youtube.com/embed/kQtSQnUCVts</video:player_loc>
<video:duration>535</video:duration>
<video:publication_date>2026-02-28T10:48:32.000Z</video:publication_date>
</video:video>
<lastmod>2026-02-28T10:48:32.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/llms-rag-and-ai-agents-the-simplest-explanation-youll-ever-find-5Q01zEDSdM0</loc>
<video:video>
<video:title>LLMs, RAG, and AI agents - the simplest explanation you&apos;ll ever find</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/5Q01zEDSdM0/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down LLMs, RAG, and AI agents in the simplest terms: LLMs are models trained on human language that turn prompts into natural-language output, but they don&apos;t know your private data or act beyond text. RAG fixes that by retrieving relevant data from your own dataset, augmenting the prompt with it, and letting the model generate an answer grounded in that context. AI agents go further by connecting an LLM to tools—plain functions like file reads/writes, web search, or custom APIs—that the model calls in a loop until the task is done. My take is that these three pieces stack: LLM for language, RAG for your data, agents for action.</video:description>
<video:player_loc>https://www.youtube.com/embed/5Q01zEDSdM0</video:player_loc>
<video:duration>360</video:duration>
<video:publication_date>2026-02-21T14:14:40.000Z</video:publication_date>
</video:video>
<lastmod>2026-02-21T14:14:40.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-fall-and-fall-of-mysql-6KrYgUDyDXI</loc>
<video:video>
<video:title>The fall (and fall) of MySQL</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/6KrYgUDyDXI/hqdefault.jpg</video:thumbnail_loc>
<video:description>MySQL is declining because Oracle treats the free open-source server as a showcase while keeping real development and the best features in its paid enterprise edition, so I lay out the evidence and the migration path. I point to MySQL&apos;s GitHub repo receiving zero open pull requests versus MariaDB&apos;s 270, commits trending down, and key capabilities like vector search and parallel query execution locked behind the enterprise version; I also cite Sysbench comparisons showing MariaDB outperforming MySQL as MySQL gets slower and more bloated. My take is that for an existing MySQL project you should switch to MariaDB because it keeps backwards compatibility in 99% of cases, and for a new project you should choose PostgreSQL as the industry-standard default.</video:description>
<video:player_loc>https://www.youtube.com/embed/6KrYgUDyDXI</video:player_loc>
<video:duration>463</video:duration>
<video:publication_date>2026-02-18T08:50:15.000Z</video:publication_date>
</video:video>
<lastmod>2026-02-18T08:50:15.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/guys-please-secure-your-file-uploads-this-is-how-oCo-OPIcfdo</loc>
<video:video>
<video:title>Guys, please secure your file uploads (this is how)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/oCo-OPIcfdo/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down the seven layers I use to secure file uploads in any API: pre-upload validation of size, MIME type, and extension; storing files in third-party object storage instead of app servers; asynchronous malware scanning and sandboxing; renaming and metadata sanitization; access control with signed, preferably expiring URLs; logging, monitoring, and rate limiting; and duplicating validation on both client and server. My main argument is that AI-assisted coding has made insecure uploads more common, but the fix is straightforward if you treat every file as untrusted at every step. I land on server-side validation as non-negotiable, because client-side checks only improve UX and can always be bypassed.</video:description>
<video:player_loc>https://www.youtube.com/embed/oCo-OPIcfdo</video:player_loc>
<video:duration>832</video:duration>
<video:publication_date>2026-02-11T16:22:37.000Z</video:publication_date>
</video:video>
<lastmod>2026-02-11T16:22:37.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-replaced-coding-heres-whats-left-now-9DxLM_m3j0A</loc>
<video:video>
<video:title>AI replaced coding, here&apos;s what&apos;s left now</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/9DxLM_m3j0A/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI is replacing junior developers who use coding assistants to bypass the fundamentals, while seniors use the same tools to amplify engineering skills they already have. I explain why clean code, DRY, edge-case handling, documentation, and problem decomposition are not just senior habits — they are what make AI-assisted coding work at all. I walk through the specific skills juniors need to become irreplaceable: writing specific prompts, forcing AI to handle edge cases, learning what clean code looks like, preventing duplication by pointing AI to existing functions, breaking large features into small verified steps, and documenting the codebase so both you and the model can maintain it. My conclusion is blunt: if you keep using AI to skip learning these skills, you will get replaced; if you learn them, you become far more valuable.</video:description>
<video:player_loc>https://www.youtube.com/embed/9DxLM_m3j0A</video:player_loc>
<video:duration>456</video:duration>
<video:publication_date>2026-02-05T12:44:29.000Z</video:publication_date>
</video:video>
<lastmod>2026-02-05T12:44:29.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/ai-coding-is-easy-actually-o5Tijawgbz0</loc>
<video:video>
<video:title>AI coding is easy, actually</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/o5Tijawgbz0/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down the bell curve most developers go through with AI coding agents: the &apos;please fix this&apos; era where 99% of developers are stuck using AI like it&apos;s still 2023 Copilot, the agent-maxing phase full of sub-agent workflows and Ralph Wagon loops that produces nothing but false productivity, and the enlightened stage where I just explain what I want in plain English, give the model the right context and specific instructions, and let it do its thing. My take is that you can skip the entire curve—pick a frontier model like Opus 4.5, tell it which files to analyze, and let it work. You don&apos;t need complicated orchestration to build real products.</video:description>
<video:player_loc>https://www.youtube.com/embed/o5Tijawgbz0</video:player_loc>
<video:duration>408</video:duration>
<video:publication_date>2026-02-03T04:48:58.000Z</video:publication_date>
</video:video>
<lastmod>2026-02-03T04:48:58.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-semantic-search-works-MKUXfWmhBXc</loc>
<video:video>
<video:title>How semantic search works</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/MKUXfWmhBXc/hqdefault.jpg</video:thumbnail_loc>
<video:description>Semantic search matches meaning rather than exact keywords, and I explain how it works under the hood using vectors, embedding models, and cosine similarity. I use the example of searching “fantasy movies” and getting Lord of the Rings, Harry Potter, or Fantastic Beasts even though those titles don’t contain the keyword, then show how keyword search fails there and how semantic search clusters similar meanings in vector space. I cover embedding models that turn text or images into vectors, the token limits that force you to chunk documents, and why you must cache embeddings in a vector database because generation is expensive. I finish with the full pipeline: convert and clean your data, chunk it, embed it, store it, embed the query with the same model, compare cosine similarity, and rank the top results. My take is that this vector-and-embedding approach is the core of modern search and chat models.</video:description>
<video:player_loc>https://www.youtube.com/embed/MKUXfWmhBXc</video:player_loc>
<video:duration>1072</video:duration>
<video:publication_date>2026-01-26T09:54:38.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-26T09:54:38.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-vibe-code-like-a-pro-O10Lnu9quLw</loc>
<video:video>
<video:title>How to vibe code like a pro</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/O10Lnu9quLw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how to use AI coding tools to ship maintainable software without letting the agent make every decision. The method is: plan the whole project first by talking through requirements, edge cases, and inconsistencies with the model, then save the result as a detailed markdown plan. I use markdown instruction files and style guides instead of repeating prompts, plan complex features in plan mode and actually read the implementation plan, code iteratively by building a feature, refactoring it, and testing it. I also commit often, branch for every feature or bug fix, and finish with don&apos;t trust, verify: unit, integration, and end-to-end tests are what make AI-written code safe.</video:description>
<video:player_loc>https://www.youtube.com/embed/O10Lnu9quLw</video:player_loc>
<video:duration>1508</video:duration>
<video:publication_date>2026-01-24T06:07:27.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-24T06:07:27.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-vibe-coding-era-is-here-and-its-here-to-stay-M72ktv9qSeo</loc>
<video:video>
<video:title>The Vibe Coding Era is here (and it&apos;s here to stay!)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/M72ktv9qSeo/hqdefault.jpg</video:thumbnail_loc>
<video:description>I make the case that AI-assisted coding is here to stay and that developers who refuse to use agentic coding tools will get left behind. I trace how even former skeptics like DHH and Linus Torvalds now use AI to build real projects, and I explain why models like Opus 4.5 changed my own mind after years of writing every line by hand. My core argument is that we should stop calling it vibe coding and treat it as supervised collaboration: guide the agent, architect the system, and review every output instead of blindly accepting errors and diffs. I show how to get started with tools like Google Anti-Gravity, Cursor, Claude Code, or OpenCode, throw serious work at them, and when output is bad, ask how to prompt better rather than concluding AI sucks. I land on AI writing most code soon, with humans guiding and reviewing.</video:description>
<video:player_loc>https://www.youtube.com/embed/M72ktv9qSeo</video:player_loc>
<video:duration>927</video:duration>
<video:publication_date>2026-01-21T09:16:29.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-21T09:16:29.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/my-thoughts-about-ai-FakAPxVQCo8</loc>
<video:video>
<video:title>My thoughts about AI</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/FakAPxVQCo8/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI coding is just another abstraction over software, and since Opus 4.5 I let AI write 80 to 90% of the code in my projects while I review it for clean structure, reuse, security, and performance. I argue that this is a net positive: like C to Python and TypeScript, AI coding sacrifices some performance and code clarity but makes software development faster and accessible to far more people. My main warning is not to abstract away security: review AI output, write tests, and actively try to break your app. I also argue that every developer, especially vibe coders, must keep lower-level skills so they can step in and write raw SQL, C, or manual fixes when the AI fails. The sloppy apps will sink; the useful ones will ship faster.</video:description>
<video:player_loc>https://www.youtube.com/embed/FakAPxVQCo8</video:player_loc>
<video:duration>1108</video:duration>
<video:publication_date>2026-01-19T10:38:50.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-19T10:38:50.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/my-experience-vibe-coding-an-entire-app-as-a-real-software-engineer-y1UWXjqP85w</loc>
<video:video>
<video:title>My experience vibe coding an entire app (as a real software engineer)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/y1UWXjqP85w/hqdefault.jpg</video:thumbnail_loc>
<video:description>I built a complete jump rope tracking app with AI writing every line of code—over 7,000 lines—and I show how I guided the model to produce clean, secure, fast code instead of vibe-coded slop. I didn&apos;t write a single line myself. I used Google Antigravity, started with a ~600-line product plan and a style guide, then built feature by feature: I reviewed every implementation plan, did my own research on third-party packages, pasted current docs when the model&apos;s training cutoff caused outdated API use, and kept Git branches per feature. My conclusion: vibe coders can&apos;t build this as fast or without pain; fundamentals matter because AI augments developers rather than replacing them.</video:description>
<video:player_loc>https://www.youtube.com/embed/y1UWXjqP85w</video:player_loc>
<video:duration>1168</video:duration>
<video:publication_date>2026-01-17T09:45:18.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-17T09:45:18.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-only-websockets-tutorial-you-need-NFe9MfyZgCY</loc>
<video:video>
<video:title>The only WebSockets tutorial you need</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/NFe9MfyZgCY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how WebSockets work by contrasting them with HTTP and showing when to use each protocol. HTTP is request-response: the client asks, and only then can the server respond. WebSockets flip that into an event protocol where both client and server can send events at any time, without waiting for a request. I demo a minimal Flask backend and native JavaScript client: the server pushes random events immediately, and the client can send messages back, with handlers for open, message, close, and error. My take is that concepts matter more than syntax now that AI writes most code. Use WebSockets for real-time, bidirectional use cases like chat, multiplayer games, and live process updates, not for every request.</video:description>
<video:player_loc>https://www.youtube.com/embed/NFe9MfyZgCY</video:player_loc>
<video:duration>607</video:duration>
<video:publication_date>2026-01-15T08:12:07.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-15T08:12:07.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/json-is-slow-use-this-instead-RHGpbtEFTRE</loc>
<video:video>
<video:title>JSON is slow, use this instead</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/RHGpbtEFTRE/hqdefault.jpg</video:thumbnail_loc>
<video:description>JSON is slow for performance-critical applications, so I benchmark a custom binary protocol against it in a multiplayer game server and show how to cut bandwidth by roughly two-thirds. I compare a JSON player-state payload at 311 bytes per player with a binary version at 104 bytes per player, then scale that to 50 players at 60 updates per second: about 0.8 MB/s with JSON versus 0.29 MB/s with binary. My take is that JSON is fine for typical REST APIs, but when every millisecond and byte matters, you should use a custom or predefined binary protocol that drops field names, braces, quotes, and commas.</video:description>
<video:player_loc>https://www.youtube.com/embed/RHGpbtEFTRE</video:player_loc>
<video:duration>770</video:duration>
<video:publication_date>2026-01-13T16:29:12.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-13T16:29:12.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-19-oauth-web-server-OTkDSiRJxOw</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 19 (oAuth web server)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/OTkDSiRJxOw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a minimal temporary web server that handles Google OAuth callbacks on localhost:8998 so I can stop copying authorization codes from the broken localhost redirect page. I update the redirect URI in client.json to point to /google-oauth-redirect, create a web package with a startHTTP function, and wire it into main with a cancelable context so the server runs only until ConnectToDrive finishes. The server uses http.Server, a graceful shutdown with a one-second timeout, and ignores http.ErrServerClosed because the shutdown is intentional. My take is that even a plain-text callback page is enough to prove the flow, and the same handler can become a polished HTML page with a copy button for real users.</video:description>
<video:player_loc>https://www.youtube.com/embed/OTkDSiRJxOw</video:player_loc>
<video:duration>718</video:duration>
<video:publication_date>2026-01-04T13:05:21.000Z</video:publication_date>
</video:video>
<lastmod>2026-01-04T13:05:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-best-way-to-integrate-django-60-with-tailwind-v4-Pn-7GN0zXW8</loc>
<video:video>
<video:title>The best way to integrate Django 6.0 with Tailwind V4</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Pn-7GN0zXW8/hqdefault.jpg</video:thumbnail_loc>
<video:description>I integrate Django 6.0 with Tailwind CSS V4 directly inside Django templates, without building a REST API or a separate React frontend. I create a Django project, manage Python dependencies with UV, initialize an npm package, install Tailwind with Bun, add a Django app, create base.html and a static globals.css that imports Tailwind, then run the Tailwind CLI in watch mode to compile output.css. My main point is that you can bring Node-based tooling into a Django app while still using Django templates and Django&apos;s built-in features.</video:description>
<video:player_loc>https://www.youtube.com/embed/Pn-7GN0zXW8</video:player_loc>
<video:duration>299</video:duration>
<video:publication_date>2025-12-30T15:46:21.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-30T15:46:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-18-connecting-the-new-gitignore-parser-wtKhL8K_wqM</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 18 (Connecting the new gitignore parser)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/wtKhL8K_wqM/hqdefault.jpg</video:thumbnail_loc>
<video:description>I connect my custom gitignore parser to the DriveSyncer sync algorithm, replacing the old string-based ignore list with a GitIgnore struct that exposes shouldIgnore. I refactor countTotalFiles and uploadFolder to stop looping over raw ignore strings, pass the GitIgnore pointer through, and switch on shouldIgnore’s result: upload when allowed, skip when ignored, and return nil for ignore-file cases. After commenting out the upload-file logs for a clean test, I run the sync and check the log file: almost everything is skipped, with only the log file and sync file changing, so no extra files get uploaded. My take is the new parser is wired in correctly and now supports complicated gitignore patterns.</video:description>
<video:player_loc>https://www.youtube.com/embed/wtKhL8K_wqM</video:player_loc>
<video:duration>377</video:duration>
<video:publication_date>2025-12-28T17:50:22.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-28T17:50:22.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-17-the-shouldignore-func-lHevPpi_qLY</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 17 (The shouldIgnore() func)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/lHevPpi_qLY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the shouldIgnore() function for my custom Gitignore parser in Go, which decides whether DriveSinker should upload a file, ignore a file, or skip an entire directory. I convert absolute paths to relative paths, normalize separators with filepath.ToSlash, and match each parsed pattern against either the full relative path or the base name depending on whether the pattern is rooted. I implement leading, trailing, and middle double-star handling, support negated patterns, and use a tri-state return type—IgnoreDir, IgnoreFile, Allow—because a boolean can&apos;t represent filepath.SkipDir. Invalid patterns are logged and skipped rather than crashing the sync. My take: a custom Gitignore implementation needs explicit directory handling and proper normalization to be reliable across platforms.</video:description>
<video:player_loc>https://www.youtube.com/embed/lHevPpi_qLY</video:player_loc>
<video:duration>719</video:duration>
<video:publication_date>2025-12-24T08:19:21.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-24T08:19:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-16-custom-gitignore-parser-tkkvW7YHnmY</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 16 (Custom gitignore parser)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/tkkvW7YHnmY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a custom .gitignore parser in Go for my DriveSyncer project so it can decide which files and folders to skip when uploading projects to Google Drive. I create ignore.go with a GitIgnore struct and an IgnorePattern struct that stores the raw pattern, negate, dirOnly, fromRoot, and a double-star mode: leading, trailing, or middle. The parser trims whitespace, skips empty and comment lines, strips ! for negation, treats leading / as root-anchored and trailing / as directory-only, and converts patterns into structured flags. I merge the global ignore list from config, optionally read the project’s .gitignore, and return patterns ready for the shouldIgnore step. I argue that parsing each rule independently is necessary because one line can combine multiple patterns, so a simple switch won’t work. The upload integration is left for the next part.</video:description>
<video:player_loc>https://www.youtube.com/embed/tkkvW7YHnmY</video:player_loc>
<video:duration>988</video:duration>
<video:publication_date>2025-12-22T06:03:07.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-22T06:03:07.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-15-bug-hunting-olovBUvGO6E</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 15 (Bug hunting)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/olovBUvGO6E/hqdefault.jpg</video:thumbnail_loc>
<video:description>I fix the bugs I introduced in the DriveSyncer project before moving on: projectExists now checks the folder name instead of matching any folder under the programming parent, I silence the noisy project-tree logs, and I refactor progress-bar rendering so zero files to upload returns 100% instead of dividing by zero. I test the Google Drive sync end to end, confirming it uploads the project, skips sensitive files like client and token JSON files and the .git folder, and skips everything on a second run when the project already exists. I also preview the upcoming custom .gitignore parser, which I need to handle complex real-world patterns instead of only literal file names.</video:description>
<video:player_loc>https://www.youtube.com/embed/olovBUvGO6E</video:player_loc>
<video:duration>715</video:duration>
<video:publication_date>2025-12-19T04:28:55.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-19T04:28:55.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/why-i-hate-javascript-l174V4JEXog</loc>
<video:video>
<video:title>Why I hate JavaScript</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/l174V4JEXog/hqdefault.jpg</video:thumbnail_loc>
<video:description>I hate JavaScript because it has no real standard library, its npm ecosystem rewards fragile micro-dependencies, and its beginner-friendly culture keeps producing unoptimized apps. I point to Go and Python as counterexamples: Go&apos;s standard library covers most needs, and Python&apos;s C-backed standard library is fast and consistent. I walk through npm absurdities like is-even and is-odd pulling hundreds of thousands of weekly downloads, plus the left-pad incident as proof that third-party packages can vanish and break everything. I argue JavaScript needs a proper standard library or agreed standards, and I tell every JS developer to learn Go, Rust, C, C#, or Java so they see performance, memory, and maintainability tradeoffs beyond npm.</video:description>
<video:player_loc>https://www.youtube.com/embed/l174V4JEXog</video:player_loc>
<video:duration>1306</video:duration>
<video:publication_date>2025-12-16T04:34:33.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-16T04:34:33.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-14-deleting-ghost-files-6wiiDClyUqE</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 14 (Deleting ghost files)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/6wiiDClyUqE/hqdefault.jpg</video:thumbnail_loc>
<video:description>Ghost files are files you&apos;ve deleted from your local project that still sit on Google Drive taking up space, and I fix that by collecting every local file and folder path during the upload walk, then comparing them against the remote paths I already track in the tracker&apos;s nodes map. Anything on Drive that has no matching local path is a ghost, and I move it to the trash with a Trashed: true update through the Drive API instead of hard-deleting it. I hook this into the end of uploadFolder and prove it out by uploading a ghost.txt, deleting it locally, and watching the next sync remove it from Drive while skipping every unchanged file. This completes the new sync algorithm, which is far more performant than deleting and re-uploading the whole project folder.</video:description>
<video:player_loc>https://www.youtube.com/embed/6wiiDClyUqE</video:player_loc>
<video:duration>821</video:duration>
<video:publication_date>2025-12-13T04:21:38.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-13T04:21:38.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-13-overwriting-files-VsKdd--IBBU</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 13 (Overwriting files)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/VsKdd--IBBU/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement overwriting existing files in my Google Drive sync algorithm so modified local files update on Drive instead of creating duplicate uploads. In the uploadFile function in api/sync.go, I query Drive by file name and parent ID, and if a match exists I call files.Update with the existing file ID and the opened local file as media, leaving metadata untouched. I test it by re-syncing the project: only sync.go has changed, and the log shows it already exists and is overriding; checking Drive confirms the new code is there. Overwriting works now, and I leave deleting ghost files for the next part.</video:description>
<video:player_loc>https://www.youtube.com/embed/VsKdd--IBBU</video:player_loc>
<video:duration>596</video:duration>
<video:publication_date>2025-12-11T07:50:19.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-11T07:50:19.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-12-1-fixing-rewrites-mt0S9G9nZ9I</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 12-1 (Fixing rewrites)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/mt0S9G9nZ9I/hqdefault.jpg</video:thumbnail_loc>
<video:description>I fix an AOF rewrite bug in my Go Redis clone where commands buffered during BGREWRITEAOF were never flushed to the rewritten file. A viewer caught that after the rewrite clears and rebuilds the AOF, any new commands written to the in-memory buffer while the file was busy are left stranded, so I add the missing step to write that buffer into the AOF file before future records are rerouted back to disk. I test it by running BGREWRITEAOF while setting new keys, and I explain why I log a buffer-write error instead of returning early: the AOF still needs to switch back to writing to the file.</video:description>
<video:player_loc>https://www.youtube.com/embed/mt0S9G9nZ9I</video:player_loc>
<video:duration>470</video:duration>
<video:publication_date>2025-12-09T06:13:10.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-09T06:13:10.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-12-ignoring-unchanged-files-sBPAbDasmAg</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 12 (Ignoring unchanged files)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/sBPAbDasmAg/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a shouldUpload method into the DriveSyncer tracker so my Google Drive sync only uploads files whose local checksum differs from the remote checksum, and skips unchanged files entirely. I show how the sync algorithm walks the remote project tree, checks whether each path already exists as a folder or file, opens the local file to compute its checksum, and returns false for existing folders or matching checksums while still uploading brand-new files. I also fix a major subfolder bug: I was keying the subfolders map by folder name, which breaks when two subfolders share a name, so I switch to full subfolder paths. Finally, I map already-uploaded remote subfolders back into the local subfolders map so new files inside existing Google Drive folders can still resolve their parent IDs. With that, the sync algorithm is essentially done.</video:description>
<video:player_loc>https://www.youtube.com/embed/sBPAbDasmAg</video:player_loc>
<video:duration>918</video:duration>
<video:publication_date>2025-12-08T05:20:38.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-08T05:20:38.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-11-mapping-local-to-remote-projects-3BeQ4Wd2r3A</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 11 (Mapping local to remote projects)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/3BeQ4Wd2r3A/hqdefault.jpg</video:thumbnail_loc>
<video:description>To stop my Drive syncer from uploading duplicate files, I build the mapping between local project folders and their Google Drive counterparts. I write a walkProjectTree function in Go that recursively walks the entire Drive project tree using a queue of folder IDs, handles pagination, and records each file and folder&apos;s ID, name, mime type, size, modified time, and SHA-256 checksum into a node map keyed by local path. That gives me a clear local-to-remote mapping I can use to tell which files were modified, which are unchanged, and which still need uploading.</video:description>
<video:player_loc>https://www.youtube.com/embed/3BeQ4Wd2r3A</video:player_loc>
<video:duration>946</video:duration>
<video:publication_date>2025-12-05T05:27:07.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-05T05:27:07.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/why-no-one-trusts-your-website-xkh26xFCFKw</loc>
<video:video>
<video:title>Why No One Trusts Your Website</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/xkh26xFCFKw/hqdefault.jpg</video:thumbnail_loc>
<video:description>Your website loses trust when its writing doesn&apos;t match your brand personality, so I break down how to make your copy sound like the brand you actually want to be. The words and language you use shape users&apos; emotions and perception: Discord can be casual and friendly because it speaks to gamers, while JPMorgan Chase sounds professional and secure because people won&apos;t trust their savings to a bank that talks like a drinking buddy. I walk through a two-step process—study your target audience and brand mission, then adjust your writing style accordingly—and show how AI can analyze and rewrite your existing copy once you know the voice you&apos;re aiming for. My take: even a well-designed site fails if the writing style is wrong.</video:description>
<video:player_loc>https://www.youtube.com/embed/xkh26xFCFKw</video:player_loc>
<video:duration>623</video:duration>
<video:publication_date>2025-12-03T03:45:24.000Z</video:publication_date>
</video:video>
<lastmod>2025-12-03T03:45:24.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/dear-ceos-ai-is-not-gonna-replace-your-developers-lt41ujdXZVY</loc>
<video:video>
<video:title>Dear CEOs, AI is not gonna replace your developers</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/lt41ujdXZVY/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI is not going to replace developers, and I explain why the current hype from Anthropic and other AI companies gets the engineering reality wrong. I break down Adam Wolf&apos;s claim that we will stop checking generated code the way we ignore compiler output: LLMs are non-deterministic by design, while compilers are deterministic, so their output can never be trusted blindly. I argue that AI can speed up coding, but it also increases technical debt because developers write more code faster and often do not understand code they did not write. That debt still has to be paid, so AI does not let teams ship good products faster. I also point to bloated, buggy software like Windows 11 as a warning, and I land on the same conclusion: augment developers with AI instead of trying to replace them.</video:description>
<video:player_loc>https://www.youtube.com/embed/lt41ujdXZVY</video:player_loc>
<video:duration>762</video:duration>
<video:publication_date>2025-11-27T12:33:03.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-27T12:33:03.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-10-new-sync-algorithm-6aluc_ItjUA</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 10 (New sync algorithm)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/6aluc_ItjUA/hqdefault.jpg</video:thumbnail_loc>
<video:description>I start building a more efficient Google Drive sync algorithm that stops deleting and reuploading an entire project folder on every sync, and I fix the upload progress bars so they reliably reach 100%. I fix the progress bar by doubling the sync.WaitGroup and calling wg.Done from the rendering goroutine, then add a tracker that checks whether a project folder already exists and computes SHA-256 checksums for files. I comment out the old delete-previous-folder step for now, which makes uploads work but create duplicates until I add per-file change tracking and deletion in the next parts. My take: the old delete-and-reupload approach gets the job done but wastes bandwidth, and incremental sync needs to be built in stages.</video:description>
<video:player_loc>https://www.youtube.com/embed/6aluc_ItjUA</video:player_loc>
<video:duration>1578</video:duration>
<video:publication_date>2025-11-26T04:47:11.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-26T04:47:11.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-9-concurrent-uploads-and-progress-bars-tklq7ucVFgs</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 9 (Concurrent uploads and progress bars)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/tklq7ucVFgs/hqdefault.jpg</video:thumbnail_loc>
<video:description>I make the DriveSyncer upload multiple Google Drive projects concurrently and add live per-project progress bars. I move each folder upload into a goroutine, use a sync.WaitGroup to run all projects simultaneously, and track progress with an UploadProgress struct sent over a channel—pointers keep the updates cheap. I redirect all log output to drive-syncer.log so the terminal stays clean, then build progress bars that show folder name, percentage, current file, and uploaded/total files. The bars use ANSI cursor movement to overwrite each line in place, and I count only real files while reusing the ignore list for node_modules and .git. I also fix the expired-token flow by falling back to the web token fetch. I land with concurrent uploads and working progress bars, plus one known bug: the program can exit before the rendered bars reach 100%.</video:description>
<video:player_loc>https://www.youtube.com/embed/tklq7ucVFgs</video:player_loc>
<video:duration>1952</video:duration>
<video:publication_date>2025-11-19T09:09:53.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-19T09:09:53.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-8-set-up-cli-cmds-T2y0zcTjHuc</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 8 (Set up CLI cmds)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/T2y0zcTjHuc/hqdefault.jpg</video:thumbnail_loc>
<video:description>I set up a Cobra CLI for my Go Google Drive syncer so I can run commands like go run . sync instead of hard-coding uploads in main.go. I install Cobra, create a cmd package with a root command, define an Execute function using cobra.CheckErr(rootCmd.Execute()), and call it from main.go. Then I add a sync subcommand that loops over the configured folders and uploads them to Google Drive, skipping .git, and register it on the root command with rootCmd.AddCommand(syncCmd). The result is a working CLI that runs the full sync process, with room to add arguments and more commands later.</video:description>
<video:player_loc>https://www.youtube.com/embed/T2y0zcTjHuc</video:player_loc>
<video:duration>482</video:duration>
<video:publication_date>2025-11-17T09:02:42.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-17T09:02:42.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-7-reorganizing-codebase-Jaw6tOoNuPQ</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 7 (Reorganizing Codebase)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Jaw6tOoNuPQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>Before building the CLI for my Drive syncer, I spend this session reorganizing the codebase, moving everything out of main.go and sync.go into proper Go packages: internal/models for shared structs, auth for OAuth, config for configuration, and api (renamed from sync) for the Drive logic. I walk through the errors this creates—capitalizing struct fields so they export across packages, and working around Go&apos;s rule that you can&apos;t define methods on non-local internal types by using a regular function with a pointer receiver. I also hit a nil pointer dereference because I forgot to call ConnectToDrive before using the Drive service, and show the fix. The upload still works after the refactor, and my point is that this kind of maintenance is just as important as shipping new features.</video:description>
<video:player_loc>https://www.youtube.com/embed/Jaw6tOoNuPQ</video:player_loc>
<video:duration>836</video:duration>
<video:publication_date>2025-11-14T07:22:45.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-14T07:22:45.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-6-refreshing-oauth-tokens-jiUgSeLR5xY</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 6 (Refreshing OAuth Tokens)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/jiUgSeLR5xY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I add automatic OAuth token refreshing to my Google Drive syncer so I can stop manually reauthorizing every time an access token expires. I build a testAccess check around a tiny drive.files.list call with pageSize 1, classify access as granted, expired, or rejected, and handle both of Google’s expiration error messages. When access is expired and I haven’t already forced a refresh, I re-create the Drive service with force refresh; then I switch getClient to oauth2.Config.TokenSource and oauth2.NewClient so valid refresh tokens silently refresh access tokens and save the new token. If access is rejected, I exit because that’s not an expiry issue and needs manual handling.</video:description>
<video:player_loc>https://www.youtube.com/embed/jiUgSeLR5xY</video:player_loc>
<video:duration>723</video:duration>
<video:publication_date>2025-11-10T09:10:11.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-10T09:10:11.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-5-reading-gitignore-files-BoVRL7nJX-8</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 5 (Reading .gitignore files)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/BoVRL7nJX-8/hqdefault.jpg</video:thumbnail_loc>
<video:description>I add .gitignore-aware filtering to my Google Drive sync tool so it skips secrets and build artifacts during upload. I build a readGitIgnore function that opens each project&apos;s .gitignore, scans it line by line with bufio.Scanner, trims whitespace, and returns the ignored file and folder names. I combine that project-specific list with a global ignore list from config.json (node_modules, .venv, .git, and similar) whenever useGitIgnore is enabled. Then I update filepath.WalkDir to compute the absolute ignored path and skip anything that equals or lives under an ignored directory, returning filepath.SkipDir for folders. In testing, this stops client.json and token.json from being uploaded to Drive, leaving only the files I actually want backed up.</video:description>
<video:player_loc>https://www.youtube.com/embed/BoVRL7nJX-8</video:player_loc>
<video:duration>915</video:duration>
<video:publication_date>2025-11-05T06:04:18.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-05T06:04:18.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-4-handling-subfolders-and-duplicates-J_Mmhms1XN4</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 4 (Handling Subfolders and Duplicates)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/J_Mmhms1XN4/hqdefault.jpg</video:thumbnail_loc>
<video:description>I add subfolder support and duplicate-folder handling to my Google Drive project syncer, so re-syncing a project replaces the old Drive folder instead of creating duplicates and preserves nested folder structure. I use a query against the Drive API that checks mimeType, folder name, parent base folder, and trashed=false, limit it with pageSize=1, then branch: no match means upload fresh, one match means delete it before re-uploading, multiple matches means return an error instead of guessing which folder to remove. For subfolders, I refactor file uploads into an uploadFile function, create missing folders with createSubfolder using the parent ID, and keep a map from subfolder name to Drive folder ID so each file lands in the right parent. I choose delete-and-reupload over per-file checksum tracking because it is much simpler, and I leave the next feature as .gitignore/config.json filtering.</video:description>
<video:player_loc>https://www.youtube.com/embed/J_Mmhms1XN4</video:player_loc>
<video:duration>1613</video:duration>
<video:publication_date>2025-11-03T07:06:27.000Z</video:publication_date>
</video:video>
<lastmod>2025-11-03T07:06:27.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-3-upload-to-drive-cyXWzYzG8is</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 3 (Upload to Drive)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/cyXWzYzG8is/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement the upload side of my Google Drive sync tool in Go, creating folders on Drive and uploading every file from a configured local folder into them. I build an uploadFolder function that creates a project folder with the application/vnd.google-apps.folder MIME type, walks the local directory with filepath.Walk, skips directories, opens each file, and sends it to Drive using drive.files.create(...).Media(file) with the project folder as its parent. I also add createBaseFolder, which queries Drive for an existing folder by MIME type, name, and trashed=false before creating a new one, because Drive identifies folders by ID and will happily create duplicates with the same name. I land on using a single configurable base folder so all synced projects stay organized, and I note that Git ignore handling and sensitive-file filtering come next.</video:description>
<video:player_loc>https://www.youtube.com/embed/cyXWzYzG8is</video:player_loc>
<video:duration>1368</video:duration>
<video:publication_date>2025-10-31T11:11:38.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-31T11:11:38.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/syncing-projects-with-google-drive-part-2-config-files-and-drive-api-WEG286vf-qs</loc>
<video:video>
<video:title>Syncing projects with Google Drive - Part 2 (Config Files and Drive API)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/WEG286vf-qs/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a config-driven Google Drive syncer in Go, cleaning up the entry point and adding a reusable Drive struct plus JSON configuration for deciding which project folders and files get uploaded. I refactor the OAuth and Drive API logic into a Drive struct with Config and *drive.Service fields, add a Connect method that takes forceRefresh and context.Context, and keep token.json cached so I don&apos;t reauthorize until the token expires. I then define a Config struct with BaseFolder, Folders, Ignore, and UseGitIgnore, a Folder struct with Name and Path, and a readConfig function that decodes config.json directly. I show how to pull default folder names from paths with filepath.Base while still allowing custom names, and I explain why JSON config is much simpler than a custom config language. I land on a clean, config-backed foundation before adding file and folder uploads next.</video:description>
<video:player_loc>https://www.youtube.com/embed/WEG286vf-qs</video:player_loc>
<video:duration>1138</video:duration>
<video:publication_date>2025-10-29T16:36:18.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-29T16:36:18.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/sync-code-with-google-drive-part-1-google-cloud-console-oauth-oxCG7PxbrJY</loc>
<video:video>
<video:title>Sync code with Google Drive - Part 1 (Google Cloud Console, OAUTH)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/oxCG7PxbrJY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a Google Drive syncer in Go, starting with the Google Cloud Console and OAuth setup so I can back up local coding projects to Drive as a second backup alongside GitHub. After losing unpushed local changes when my hard drive corrupted, I wanted automatic Drive backups that can skip files like .git, node_modules, .env, and venvs, since the Google Drive desktop app can&apos;t ignore folders. I create a Google Cloud project, enable the Drive API, configure the OAuth consent screen, add myself as a test user, download desktop client credentials, and write the Go OAuth flow that saves token.json and lists the first 10 Drive files. This part ends with auth working; uploads and ignore rules come next.</video:description>
<video:player_loc>https://www.youtube.com/embed/oxCG7PxbrJY</video:player_loc>
<video:duration>1798</video:duration>
<video:publication_date>2025-10-27T07:13:27.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-27T07:13:27.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/stop-scaling-text-sizes-proportionally-0rOS7vJ954g</loc>
<video:video>
<video:title>Stop scaling text sizes proportionally</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/0rOS7vJ954g/hqdefault.jpg</video:thumbnail_loc>
<video:description>Proportional font scaling breaks responsive typography, because headings need to shrink much faster than body text. I demonstrate with a sample article: a large heading drops from 60px to 30px (50%), a subheading from 30px to 20px (33%), while body text goes from 18px to 16px—just 2px. My take is that there is no hidden ratio connecting these elements; resize each typographic element independently and remember the only rule: larger text shrinks more and faster than smaller text.</video:description>
<video:player_loc>https://www.youtube.com/embed/0rOS7vJ954g</video:player_loc>
<video:duration>329</video:duration>
<video:publication_date>2025-10-24T05:27:40.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-24T05:27:40.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/memory-management-in-go-part-2--TL1Ar3hfDw</loc>
<video:video>
<video:title>Memory Management in Go - Part 2</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/-TL1Ar3hfDw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how Go&apos;s garbage collector manages heap memory, covering its non-generational, concurrent, tricolor mark-and-sweep design. I walk through why Go skips generational collection: the compiler keeps most values on the stack, so the generational hypothesis buys little, while a non-generational collector stays simpler. I show how mark-and-sweep works, including the brief stop-the-world at the start of marking to set up the write barrier, then the concurrent tricolor marking phase where white objects are unused, blue objects are being processed, and brown objects are in use, followed by the sweep phase that frees white objects. I also explain the GOGC environment variable: default 100 triggers GC when heap memory doubles since the last run, you can tune it, and you can set it to off, though I don&apos;t see a practical reason to disable GC.</video:description>
<video:player_loc>https://www.youtube.com/embed/-TL1Ar3hfDw</video:player_loc>
<video:duration>1308</video:duration>
<video:publication_date>2025-10-22T08:03:24.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-22T08:03:24.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/memory-management-in-go-part-1-kRWb9n2jGPY</loc>
<video:video>
<video:title>Memory Management in Go - Part 1</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/kRWb9n2jGPY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down how Go manages memory under the hood: the stack, the heap, and the escape analysis rules the compiler uses to decide where variables live. I explain why stack memory is automatically managed, LIFO, and fast, while heap memory is slower and requires Go&apos;s garbage collector, and I walk through the escape analysis cases that force values onto the heap: returning pointers, using maps/slices/structs, closures that reference outer variables, and values stored inside complex objects. My take is that if you care about performance-critical Go, you need to understand these rules to reduce heap allocations and garbage collector overhead.</video:description>
<video:player_loc>https://www.youtube.com/embed/kRWb9n2jGPY</video:player_loc>
<video:duration>1172</video:duration>
<video:publication_date>2025-10-20T10:59:15.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-20T10:59:15.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-20-finish-info-fix-aof-memory-BzdCkr4ssHs</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 20 (Finish INFO &amp; fix AOF memory)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/BzdCkr4ssHs/hqdefault.jpg</video:thumbnail_loc>
<video:description>I finish the INFO command and fix AOF memory handling in my Go Redis clone, wrapping up the 20-part series. I add the persistence and general INFO categories, which means tracking RDB last-save time and save count, AOF rewrite count and in-progress state, total connections, commands processed, expired keys, and evicted keys. I show where each stat belongs: update RDB/AOF stats at save/rewrite time, increment connection and command counters in the main handlers, and count expired/evicted keys inside the DB layer. I also fix the TTL handler to call tryExpire so expirations are recorded, and I change AOF sync to accept only maxmemory, eviction policy, and mem samples, so restored AOF records respect the memory limit without being re-appended. The result is a complete INFO output and an AOF layer that evicts correctly under maxmemory.</video:description>
<video:player_loc>https://www.youtube.com/embed/BzdCkr4ssHs</video:player_loc>
<video:duration>1500</video:duration>
<video:publication_date>2025-10-17T14:47:22.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-17T14:47:22.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-19-info-command-tVBdJiQYroo</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 19 (INFO command)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/tVBdJiQYroo/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build out the INFO command for my Go Redis clone, adding the server, clients, and memory sections while leaving persistence and general stats for the final part. I create an Info struct and a build method that pulls from app state, then return fields like redis_version, process_id, tcp_port, server_time_usec, uptime_in_seconds, executable, and config_file. For clients I track connected_clients by incrementing on new connections and decrementing with defer. For memory I use go-psutil v4.25.8 to get total system memory because Go has no built-in API for it, track used_memory and used_memory_peak, and expose maxmemory and maxmemory_policy from config. I also add app-state fields for server start time, client count, and peak memory, and update peak memory inside db.set. The INFO handler itself comes next.</video:description>
<video:player_loc>https://www.youtube.com/embed/tVBdJiQYroo</video:player_loc>
<video:duration>934</video:duration>
<video:publication_date>2025-10-15T09:25:08.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-15T09:25:08.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-18-monitor-command-xZzEGyRsU1s</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 18 (MONITOR command)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/xZzEGyRsU1s/hqdefault.jpg</video:thumbnail_loc>
<video:description>I fix a missing mutex unlock in the GET path of my Go Redis clone, refactor the growing codebase into focused files, and implement the MONITOR command so one client can observe every command other clients run. Along the way, I explain why a deferred read-unlock is the safe choice when the lock covers the whole function, but why I still release locks manually before long non-database work. I also walk through how I would implement volatile eviction policies with a second expirable-key store, though I leave that as an exercise. For MONITOR, I add a monitors list to app state, append and remove clients on connect/disconnect, relay every command to all monitors except the sender in a goroutine, and format messages with Unix timestamp, client address, and quoted command arguments.</video:description>
<video:player_loc>https://www.youtube.com/embed/xZzEGyRsU1s</video:player_loc>
<video:duration>1202</video:duration>
<video:publication_date>2025-10-13T08:59:04.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-13T08:59:04.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-17-allkeys-lru-and-allkeys-lfu-5378onfQ-7I</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 17 (allkeys-lru and allkeys-lfu)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/5378onfQ-7I/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement the allkeys-lru and allkeys-lfu eviction policies in my Go Redis clone by sorting the sampled keys by last access time for LRU or access count for LFU, then calling evictUntilMemoryFree until enough memory is reclaimed. The implementation is short because the item metadata and eviction helper are already in place. I also note that AOF synchronization currently bypasses maxmemory because it runs with a blank config, which I plan to fix, and I outline how the remaining volatile policies will work: they only target expirable keys, with volatile-ttl evicting the key closest to expiry first.</video:description>
<video:player_loc>https://www.youtube.com/embed/5378onfQ-7I</video:player_loc>
<video:duration>515</video:duration>
<video:publication_date>2025-10-11T16:04:17.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-11T16:04:17.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-16-fixing-aof-file-sync-zPE2oBa0VLw</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 16 (Fixing AOF file sync)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/zPE2oBa0VLw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I fix the AOF restore bug in my Go Redis clone where the server only loaded the first key from the append-only file and ignored every command after it. The root cause was that readArray created a fresh bufio.Reader for every array read; that works for network clients sending one command at a time, but it desynchronizes an AOF replay containing multiple commands. I change readArray to accept a shared *bufio.Reader, create one reader for the AOF file and one per connection, and make readBulk return errors so replay can stop on a bad command. After the fix, restoring three keys works: memory climbs 98→191→292 and KEYS returns name, age, and job.</video:description>
<video:player_loc>https://www.youtube.com/embed/zPE2oBa0VLw</video:player_loc>
<video:duration>340</video:duration>
<video:publication_date>2025-10-08T14:12:48.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-08T14:12:48.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-15-evicting-keys-allkeys-random-eviction-cpYnCe8tg3A</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 15 (Evicting Keys &amp; allkeys-random eviction)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/cpYnCe8tg3A/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement the allkeys-random eviction policy for my Go Redis clone: when the database is out of memory, I sample a configurable number of keys and delete them until enough memory is freed for the new write. I add max-memory-samples to the config, list the other eviction policies such as allkeys-lru, allkeys-lfu, volatile-lru, volatile-lfu and volatile-ttl, and lay groundwork for them by renaming the Key struct to Item and tracking each item&apos;s last access time and access count. I also add DB.Get, tryExpire and shouldExpire helpers, then build sampleKeys, enoughMemoryFreed and evictUntilMemoryFreed. My take: Redis-style eviction is deliberately approximate—sampling avoids scanning millions of keys—and allkeys-random is the simplest policy to get working first.</video:description>
<video:player_loc>https://www.youtube.com/embed/cpYnCe8tg3A</video:player_loc>
<video:duration>1292</video:duration>
<video:publication_date>2025-10-06T15:13:37.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-06T15:13:37.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-14-managing-memory-evictions-SRwxcxPso64</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 14 (Managing Memory &amp; Evictions)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/SRwxcxPso64/hqdefault.jpg</video:thumbnail_loc>
<video:description>I add approximate memory tracking and a maxmemory limit to my Go Redis clone, then enforce a no-eviction policy that rejects writes when the server runs out of memory. I calculate each key&apos;s approximate usage by adding Go overheads—16-byte string headers, 24-byte expiry time, 32-byte map entry metadata—plus key and value lengths, rather than chasing exact byte counts. I wire maxmemory parsing for bytes/KB/MB/GB into config as int64, update db.memory on every set and delete, and subtract old memory when overwriting a key. I also add an evictKeys hook for future policies, but for now no eviction returns a max memory reached error; I fix the mutex leak that happened on that error path.</video:description>
<video:player_loc>https://www.youtube.com/embed/SRwxcxPso64</video:player_loc>
<video:duration>1538</video:duration>
<video:publication_date>2025-10-03T06:25:06.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-03T06:25:06.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-13-database-transactions-5u_hXPMchVQ</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 13 (Database Transactions)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/5u_hXPMchVQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement Redis-style database transactions in my Go Redis clone by adding MULTI, EXEC, and DISCARD command handlers. I add a transaction struct to the app state, queue commands inside the handle function after MULTI, and return QUEUED; EXEC runs each queued handler and returns an array of replies; DISCARD clears the transaction. I also preallocate the replies slice to the command count and assign by index instead of using append, because that avoids repeated reallocations and is more performant. My take is that Redis transactions are much simpler than MySQL or Postgres transactions: they queue and execute commands together rather than offering full transactional rollback.</video:description>
<video:player_loc>https://www.youtube.com/embed/5u_hXPMchVQ</video:player_loc>
<video:duration>993</video:duration>
<video:publication_date>2025-10-01T09:50:29.000Z</video:publication_date>
</video:video>
<lastmod>2025-10-01T09:50:29.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-12-rewriting-aof-files-ZaOeUgdjcRQ</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 12 (Rewriting AOF files)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/ZaOeUgdjcRQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the BGREWRITEAOF command for my Go Redis clone, which compacts an append-only file by replacing old, outdated SET commands with only the latest key values. I demonstrate the problem by setting the same key to Hassan, Michael, and Elon: replaying the AOF executes every SET, even though only the final value matters. I implement the rewrite in the background with a read-locked copy of the database, reroute new AOF records to a memory buffer during the rewrite, truncate and seek the file to zero, write each current key as a RESP SET array, flush once, then point the AOF writer back at the file. I also fix my RESP parser, which only handled lengths under 10 characters by reading a single byte, by adding a readLine helper that parses full length lines and trimming newline characters. The result is a smaller, cleaner AOF that restores faster and avoids unnecessary command replay.</video:description>
<video:player_loc>https://www.youtube.com/embed/ZaOeUgdjcRQ</video:player_loc>
<video:duration>1360</video:duration>
<video:publication_date>2025-09-29T14:59:36.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-29T14:59:36.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-11-expire-and-ttl-1qaqafxdctI</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 11 (EXPIRE and TTL)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/1qaqafxdctI/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the EXPIRE and TTL commands into my Go Redis clone by refactoring the database store from map[string]string to map[string]*Key, where each key holds its value and an expiry time.Time. I add database-level Set and Delete helpers, implement EXPIRE with key/seconds arguments and TTL with Redis-compatible return codes, and wire lazy expiration into GET and TTL so expired keys are deleted and treated as missing. Along the way I fix a nil-pointer crash caused by reading key.exp before checking whether the key exists, and I explain why mutex locks should cover only shared-store access. I also note that Redis uses lazy checks plus a background sampling process rather than deleting keys exactly at expiry, and I leave that background sweeper for a future part.</video:description>
<video:player_loc>https://www.youtube.com/embed/1qaqafxdctI</video:player_loc>
<video:duration>1903</video:duration>
<video:publication_date>2025-09-26T14:03:06.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-26T14:03:06.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-10-auth-and-passwords-Q1HbYo9ZOtw</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 10 (AUTH and Passwords)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Q1HbYo9ZOtw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement AUTH and password protection in my Go Redis clone by adding requirepass, tracking per-client authentication, and blocking unauthenticated commands. I add requirepass dolphins to the config, parse it into a requirePass boolean and password string, create a Client struct with net.Conn and an authenticated flag, and thread that client through every command handler. The AUTH command takes exactly one password argument, returns OK or invalid password, and flips the client’s authenticated state. Unauthenticated clients can only run AUTH and COMMAND; everything else gets NOAUTH Authentication required. I also fix invalid commands so they return an error instead of hanging the client, and I note that Redis ACLs go much further than this basic password check.</video:description>
<video:player_loc>https://www.youtube.com/embed/Q1HbYo9ZOtw</video:player_loc>
<video:duration>1178</video:duration>
<video:publication_date>2025-09-24T14:43:29.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-24T14:43:29.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-9-rdb-checksum-protection-gi7sd96BfcM</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 9 (RDB Checksum Protection)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/gi7sd96BfcM/hqdefault.jpg</video:thumbnail_loc>
<video:description>I add SHA-256 checksum protection to my Go Redis clone&apos;s RDB save path so every snapshot is verified against what actually lands on disk. I encode the database into an in-memory buffer, hash it, write the bytes, flush with file.Sync(), seek back to the start, hash the file, and compare both checksums; a mismatch means the RDB file wasn&apos;t saved correctly. I also explain why the buffer and file cursors need resetting before hashing, and I defer the AUTH command and password protection to the next part.</video:description>
<video:player_loc>https://www.youtube.com/embed/gi7sd96BfcM</video:player_loc>
<video:duration>977</video:duration>
<video:publication_date>2025-09-23T07:44:25.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-23T07:44:25.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-8-dbsize-and-flushdb-WCvklkjN0K0</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 8 (DBSIZE and FLUSHDB)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/WCvklkjN0K0/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement the DBSIZE and FLUSHDB commands in my Go Redis clone, showing how to return the number of keys and wipe the entire database safely. For DBSIZE, I take a read lock and return the length of the in-memory map; for FLUSHDB, I argue the right approach is to replace the map with a fresh empty one instead of looping over and deleting every key, which would be terrible for large datasets. I test both commands against an AOF-loaded database: DBSIZE reports the persisted keys, FLUSHDB returns OK and brings DBSIZE to zero, and GET returns nil after the flush.</video:description>
<video:player_loc>https://www.youtube.com/embed/WCvklkjN0K0</video:player_loc>
<video:duration>489</video:duration>
<video:publication_date>2025-09-20T08:41:36.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-20T08:41:36.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-7-save-and-bgsave-UokU8HIQ1LA</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 7 (SAVE and BGSAVE)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/UokU8HIQ1LA/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement the SAVE and BGSAVE commands in my Go Redis clone, after tightening up the RDB snapshot code and fixing concurrency and file-handling bugs. I add a read lock around RDB encoding, switch the save file to O_TRUNC so every snapshot overwrites stale data, and fix syncRDB so it only opens an existing file and always closes it on errors. SAVE is synchronous and blocks writes while the whole database is encoded, so I treat BGSAVE as the better production choice. My BGSAVE copies the database map under a read lock, sets bgSaveRunning, launches a goroutine to save that copy, and resets state when done. I also explain why Go can&apos;t do Redis-style copy-on-write: the GC blocks that OS memory trick, so this BGSAVE still pays for a full copy, but encoding no longer holds the database lock.</video:description>
<video:player_loc>https://www.youtube.com/embed/UokU8HIQ1LA</video:player_loc>
<video:duration>1258</video:duration>
<video:publication_date>2025-09-18T16:11:05.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-18T16:11:05.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-6-del-exists-keys-concurrency-Q0hGUcwUgi8</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 6 (DEL, EXISTS, KEYS, &amp; Concurrency)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Q0hGUcwUgi8/hqdefault.jpg</video:thumbnail_loc>
<video:description>I fix my Go Redis clone’s concurrency model and implement the DEL, EXISTS, and KEYS commands. I move connection acceptance into an infinite loop, handle each client in its own goroutine with a WaitGroup, and close connections gracefully so the server no longer crashes or rejects future clients. Then I add integer replies and build DEL to return the count of keys actually deleted, EXISTS to count matching keys with a read lock, and KEYS to return keys matching a glob-style pattern using filepath.Match. I also point out that KEYS is expensive in production, where SCAN is the better choice.</video:description>
<video:player_loc>https://www.youtube.com/embed/Q0hGUcwUgi8</video:player_loc>
<video:duration>1606</video:duration>
<video:publication_date>2025-09-15T10:15:40.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-15T10:15:40.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-5-rdb-data-persistence-xx7dBgrj86Y</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 5 (RDB Data Persistence)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/xx7dBgrj86Y/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build RDB data persistence into my Go Redis clone by snapshotting the whole in-memory store to disk and loading it back on server start. I set up snapshot trackers backed by time.Ticker so each configured save rule tracks how many keys changed within its interval, increment those counters from SET when RDB is enabled, and trigger saveRDB once the changed-key threshold is met. For the snapshot itself, I use Go&apos;s gob encoder to serialize the store directly to backup.rdb instead of implementing real Redis&apos;s byte-level RDB format, and on startup syncRDB decodes that gob file straight back into database.store. This gives the clone working AOF and RDB persistence side by side.</video:description>
<video:player_loc>https://www.youtube.com/embed/xx7dBgrj86Y</video:player_loc>
<video:duration>1468</video:duration>
<video:publication_date>2025-09-13T06:26:20.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-13T06:26:20.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-4-append-only-data-persistence-8zTugfGk9OM</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 4 (Append-only Data Persistence)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/8zTugfGk9OM/hqdefault.jpg</video:thumbnail_loc>
<video:description>I implement append-only file (AOF) persistence for my Go Redis clone in Part 4, comparing Redis&apos;s AOF and RDB approaches and building the AOF side. I record every SET command as a RESP array in backup.aof using the existing writer, then flush it according to the fsync mode: always flushes after each write, everysec uses a one-second ticker goroutine, and no lets the OS decide. I also show why network replies must be flushed immediately even though file writes can stay buffered, and I add a startup sync routine that reads each AOF record and replays the SET command against a blank app state so it doesn&apos;t write new AOF entries. AOF persistence now works end to end; RDB comes next.</video:description>
<video:player_loc>https://www.youtube.com/embed/8zTugfGk9OM</video:player_loc>
<video:duration>1912</video:duration>
<video:publication_date>2025-09-10T07:19:53.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-10T07:19:53.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-3-concurrent-server-config-parser-x36_1A-g4d8</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 3 (Concurrent Server, Config Parser)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/x36_1A-g4d8/hqdefault.jpg</video:thumbnail_loc>
<video:description>I add concurrency support to my Go Redis clone with a read-write mutex and build a minimal redis.conf parser that will drive data persistence. I replace the bare map with a Database struct that holds the store and a sync.RWMutex, using RLock for GET and Lock for SET so multiple clients can&apos;t race on the same keys. I then write redis.conf with dir, appendonly, appendfilename, appendfsync, save, and dbfilename settings, and parse it line by line into a Config struct with RDBSnapshot and FsyncMode types. The parser handles save, dir, dbfilename, appendfilename, appendfsync, and appendonly, creates the configured data directory with 0755, and falls back to defaults if the file can&apos;t be read. I refactor the growing main file into db.go, handlers.go, writer.go, and value.go, then verify SET and GET still work through redis-cli. I haven&apos;t wired up multiple connections in main yet, but the mutex is in place for when I do.</video:description>
<video:player_loc>https://www.youtube.com/embed/x36_1A-g4d8</video:player_loc>
<video:duration>1484</video:duration>
<video:publication_date>2025-09-08T09:05:34.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-08T09:05:34.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-2-get-set-writing-to-client-LUYp_q63x0I</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 2 (GET, SET, Writing to client)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/LUYp_q63x0I/hqdefault.jpg</video:thumbnail_loc>
<video:description>I recap RESP and then build the GET and SET command handlers plus the client reply writer for my Go Redis clone, turning the server into a working in-memory key-value store. I go through RESP&apos;s core types: + for simple strings, - for errors, $ for bulk strings with an explicit byte length, and * for arrays of bulk strings, all terminated with . I then define a Handler type, a handlers map, and a handle function that reads the command from the first array element and dispatches to the right handler. Replies go through a Writer that serializes my Value structs back into RESP, including a null bulk string of length -1 for missing keys. I implement GET and SET against an in-memory map, add a basic COMMAND handler so redis-cli can start, and flush buffered writes. It works as a basic key-value store; concurrency and persistence come next.</video:description>
<video:player_loc>https://www.youtube.com/embed/LUYp_q63x0I</video:player_loc>
<video:duration>1695</video:duration>
<video:publication_date>2025-09-05T10:15:42.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-05T10:15:42.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/building-a-redis-clone-in-go-part-1-tcp-server-resp-protocol-djbm4xlk2w4</loc>
<video:video>
<video:title>Building a Redis clone in Go - Part 1 (TCP server, RESP protocol)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/djbm4xlk2w4/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a Redis clone in Go from scratch, starting with a TCP server on port 6379 and a parser for RESP, the Redis Serialization Protocol. I install Redis to get the official redis-cli, stop the bundled Redis server, then use the client against my own server to see raw RESP messages. I show how every client request arrives as an array of bulk strings, how to read the array length and each bulk string length, strip the trailing CRLF, and represent the parsed command in a tagged Value struct. I also wrap connection handling in an infinite loop so the server accepts repeated commands, and I land on a working RESP parser ready for actual key-value commands in part two.</video:description>
<video:player_loc>https://www.youtube.com/embed/djbm4xlk2w4</video:player_loc>
<video:duration>1199</video:duration>
<video:publication_date>2025-09-04T09:48:18.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-04T09:48:18.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-pick-perfect-icon-libraries-0Xp9c2ce5N0</loc>
<video:video>
<video:title>How to pick perfect icon libraries</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/0Xp9c2ce5N0/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to pick the right icon library for a design project, and my core argument is to choose one large, consistent, SVG-based pack instead of mixing libraries. I compare Material Icons, Bootstrap Icons, Lucide, Heroicons and others to show how subtle differences in corner radius and shape language create an inconsistent, amateurish look when mixed. I recommend sticking to a single icon library, preferably one with thousands of icons like Material Symbols rather than a few hundred, so you don&apos;t get stuck searching for a missing icon. I also argue for SVG icons over font icons because they&apos;re easier to customize and animate, and I show why you should pick packs that offer outlined, filled, rounded and sharp states for active versus inactive UI. My take: consistency, size, SVG flexibility and state variants matter most.</video:description>
<video:player_loc>https://www.youtube.com/embed/0Xp9c2ce5N0</video:player_loc>
<video:duration>518</video:duration>
<video:publication_date>2025-09-01T10:49:25.000Z</video:publication_date>
</video:video>
<lastmod>2025-09-01T10:49:25.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-easy-trick-to-creating-perfect-layouts-every-time-4RAVNeL8wTw</loc>
<video:video>
<video:title>The easy trick to creating perfect layouts every time</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/4RAVNeL8wTw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to create perfect layouts by never using more space than necessary: every element has an ideal width, so I set exact widths or max-widths instead of forcing content to fill the screen. I demonstrate this with a login form constrained to 576px, which looks terrible when stretched and clean when centered, and with a to-do app sidebar fixed at 256px instead of a relative grid column. I argue that CSS Grid can mislead you because its columns are percentage-based; a four-column grid made the sidebar 25% of the viewport and looked fine on a normal screen but absurdly wide on a large one. My take: use the ideal width for each component, leave empty background space, and don&apos;t over-design just to fill the page.</video:description>
<video:player_loc>https://www.youtube.com/embed/4RAVNeL8wTw</video:player_loc>
<video:duration>678</video:duration>
<video:publication_date>2025-08-29T07:55:47.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-29T07:55:47.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-secret-to-emphasizing-elements-properly-visual-hierarchy-tricks-tu-72TUMOxo</loc>
<video:video>
<video:title>The secret to emphasizing elements properly - visual hierarchy tricks</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/tu-72TUMOxo/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how to emphasize an active element by de-emphasizing the inactive ones around it, using a testimonial carousel as the example. I reduce the opacity and size of every inactive testimonial so it blends into the white background, while the active testimonial only gets a modest size increase. I demonstrate that when you remove the de-emphasis, the active item becomes hard to identify even though it is larger, and I argue that when you&apos;ve already maxed out an element&apos;s size or weight, the best next move is to lower the emphasis on related elements. This trick works best for lists or series where only one or two items can be active at a time.</video:description>
<video:player_loc>https://www.youtube.com/embed/tu-72TUMOxo</video:player_loc>
<video:duration>441</video:duration>
<video:publication_date>2025-08-28T08:06:12.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-28T08:06:12.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/stop-fcking-up-dark-mode-lnBbdq9p9vM</loc>
<video:video>
<video:title>Stop f*cking up dark mode</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/lnBbdq9p9vM/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to design a dark mode interface that doesn’t look like a naive color inversion, using four tactics I apply in real components. I show why pure black backgrounds and pure white foregrounds create harsh contrast, why outlined icons and softer text colors work better, and how I lower image brightness while increasing contrast so images sit naturally on dark surfaces. I also wire up the prefers-color-scheme media query—via Tailwind’s dark variant—so the correct theme is applied automatically. My take: dark mode is won through hundreds of small contrast and comfort adjustments, not one inverted palette.</video:description>
<video:player_loc>https://www.youtube.com/embed/lnBbdq9p9vM</video:player_loc>
<video:duration>654</video:duration>
<video:publication_date>2025-08-22T10:27:45.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-22T10:27:45.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/semantic-styling-is-ruining-your-designs-L-k0Thyx13k</loc>
<video:video>
<video:title>Semantic styling is ruining your designs</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/L-k0Thyx13k/hqdefault.jpg</video:thumbnail_loc>
<video:description>Style buttons and every other user action by visual hierarchy rather than semantic color meanings, because semantic colors give every action the same weight and leave users unsure what to do. I compare two identical UIs: one with green, blue, and red semantic buttons, and one where preview is primary, edit is secondary, and delete is tertiary and link-like, then live-code the hierarchy. I argue visual hierarchy is the most important part of any design, that delete should usually be the least prominent action, and that semantic colors only make sense when there is a single action. If your brand color clashes with semantics, like a red save button, I use black, white, or gray instead.</video:description>
<video:player_loc>https://www.youtube.com/embed/L-k0Thyx13k</video:player_loc>
<video:duration>825</video:duration>
<video:publication_date>2025-08-21T07:48:25.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-21T07:48:25.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/learning-neovim-from-scratch-p1-OthHup1xaA4</loc>
<video:video>
<video:title>Learning neovim from scratch p1</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/OthHup1xaA4/hqdefault.jpg</video:thumbnail_loc>
<video:description>I start learning Neovim from scratch by installing it in WSL Debian and building my own config rather than using a prebuilt distribution like LazyVim or Kickstart. I set up ~/.config/nvim, create init.lua and modular Lua files, configure options like relative line numbers, and use lazy.nvim to install nvim-treesitter. I argue prebuilt configs are great for productivity but hide how Neovim actually works, so I type most setup by hand to learn Lua and the editor&apos;s mechanics. By the end I get syntax highlighting working, though my Tab leader key is not working yet, and I plan to continue in part 2. My take remains that VS Code is the best IDE out of the box, but I want to make Neovim my main editor.</video:description>
<video:player_loc>https://www.youtube.com/embed/OthHup1xaA4</video:player_loc>
<video:duration>2767</video:duration>
<video:publication_date>2025-08-18T20:28:26.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-18T20:28:26.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/this-is-how-you-actually-create-good-visual-hierarchy-gHVnpDIv4qY</loc>
<video:video>
<video:title>This is how you actually create good visual hierarchy</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/gHVnpDIv4qY/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how to create good visual hierarchy by deciding what actually matters in a component and emphasizing it, instead of giving every label and value equal weight. I start with a profile card where labels like Name, Job, Company make everything look equally important, then remove them and let context—Aaron Smith next to a photo, Software Engineer at Google below it—do the work. I compare that with Bionic&apos;s stat blocks, where the data is large and bold while labels and icons are de-emphasized as clarifying text, and with GSM Arena&apos;s phone spec pages, where the labels become the dominant element because users scan for &apos;camera&apos; or &apos;battery,&apos; not raw values. My take: always establish hierarchy first; emphasize data when it can stand alone, labels when it cannot, and never fall back to a flat label–value layout.</video:description>
<video:player_loc>https://www.youtube.com/embed/gHVnpDIv4qY</video:player_loc>
<video:duration>1377</video:duration>
<video:publication_date>2025-08-18T15:21:48.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-18T15:21:48.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/choosing-bw-headline-fonts-and-body-fonts-_eF6Dmy3MXI</loc>
<video:video>
<video:title>Choosing b/w headline fonts and body fonts</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/_eF6Dmy3MXI/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to choose between headline fonts and body fonts by comparing x-height and letter spacing. Headline fonts like Jost tend to have a shorter x-height and tighter spacing, which looks better at large sizes; body fonts like Montserrat have a taller x-height and wider spacing, which improves readability at small sizes. I also cover display fonts: they belong only in large headlines because their striking shapes destroy body-text readability. My take is simple: don&apos;t overcomplicate it—match the font to the job and you&apos;re already ahead of most designers.</video:description>
<video:player_loc>https://www.youtube.com/embed/_eF6Dmy3MXI</video:player_loc>
<video:duration>654</video:duration>
<video:publication_date>2025-08-15T08:44:50.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-15T08:44:50.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/creating-an-electronreact-dev-environment-with-webpack-QtxvjvhhIak</loc>
<video:video>
<video:title>Creating an electron+react dev environment with webpack</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/QtxvjvhhIak/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a desktop app starter template that combines Electron, React, TypeScript, Tailwind, and Webpack, and I show how the main and renderer processes fit together from development to a distributable .exe. I set up separate Webpack configs for Electron&apos;s main and renderer processes, use electron-reloader and webpack-dev-server for hot reload, and wire npm scripts with concurrently, wait-on, and cross-env so React runs on localhost:3000 before Electron loads it. For production, Electron loads the built dist/renderer/index.html instead. I then configure electron-builder in package.json to package Windows, macOS, and Linux targets. My take: build this from scratch once so you understand what the templates hide, and pin Electron to 37.2.4 if electron-builder breaks after a minor update. The result is a working dev loop and an installable app you can distribute.</video:description>
<video:player_loc>https://www.youtube.com/embed/QtxvjvhhIak</video:player_loc>
<video:duration>3824</video:duration>
<video:publication_date>2025-08-04T08:40:23.000Z</video:publication_date>
</video:video>
<lastmod>2025-08-04T08:40:23.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-systemize-your-typography-ulenAatZJm4</loc>
<video:video>
<video:title>How to systemize your typography</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/ulenAatZJm4/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down how I systemize typography into four decisions: font families, font sizes, letter spacing, and line height. I start with font families: pick a category that matches the brand&apos;s industry and vibe, filter Google Fonts, prefer fonts with 10+ styles so bold and italic are real rather than browser-synthesized, add CSS fallbacks, and use system fonts for internal apps where custom fonts only slow things down. For font sizes, I use a hand-picked exponential scale with more small options than large ones and define it in rems, not ems. For letter spacing and line height, I give the rules rather than fixed values: tighten large headlines, keep body spacing normal or slightly wider, and scale line height inversely with text size—more for small body text and long lines, less for headlines. The result is a typography system with enough options to be creative but not so many that you get stuck.</video:description>
<video:player_loc>https://www.youtube.com/embed/ulenAatZJm4</video:player_loc>
<video:duration>803</video:duration>
<video:publication_date>2025-07-18T13:05:21.000Z</video:publication_date>
</video:video>
<lastmod>2025-07-18T13:05:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/write-your-text-in-rems-thank-me-later-4QHi4bSIOpM</loc>
<video:video>
<video:title>Write your text in rems. Thank me later.</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/4QHi4bSIOpM/hqdefault.jpg</video:thumbnail_loc>
<video:description>Rems are the only unit I use for typography, because a rem is a ratio against the root HTML font size — 16px by default — so it scales with the user&apos;s browser font-size setting and zoom level, while pixels ignore both. I show how changing the default font size in Firefox and zooming to 200% multiplies the value of one rem, why WCAG&apos;s 200%-zoom minimum is only a floor, and why you should also set the margins between your paragraphs and headings in rems so spacing scales with the text. If you want to go further, pair rems with the clamp() function for fluid responsive type.</video:description>
<video:player_loc>https://www.youtube.com/embed/4QHi4bSIOpM</video:player_loc>
<video:duration>315</video:duration>
<video:publication_date>2025-07-07T10:47:47.000Z</video:publication_date>
</video:video>
<lastmod>2025-07-07T10:47:47.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/why-hsl-colors-are-the-best-Cd7OK8mCXNs</loc>
<video:video>
<video:title>Why HSL colors are the best</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Cd7OK8mCXNs/hqdefault.jpg</video:thumbnail_loc>
<video:description>HSL is the best color format in CSS because hue, saturation and lightness map directly to how we humans perceive color, unlike hex codes and RGB values you can&apos;t decode in your head. I break down all three properties: hue as your 0–360 position on the color wheel, saturation as how much color is in the mix (0% is pure gray), and lightness as how bright or dark the color is (0% pure black, 100% pure white). I show why saturation hits hardest at 50% lightness, how hue alone tells you whether a color is warm or cool, and why changing hue does nothing at 0% saturation. My take: switch to HSL in whatever tool you use — CSS, Figma, Sketch or a site builder — and you&apos;ll visualize and reason about your colors far more easily.</video:description>
<video:player_loc>https://www.youtube.com/embed/Cd7OK8mCXNs</video:player_loc>
<video:duration>430</video:duration>
<video:publication_date>2025-07-05T06:14:14.000Z</video:publication_date>
</video:video>
<lastmod>2025-07-05T06:14:14.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/stop-over-using-color-youre-ruining-your-design-accessibility-mAKvXGzChns</loc>
<video:video>
<video:title>Stop over-using color, you&apos;re ruining your design accessibility</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/mAKvXGzChns/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to stop over-using color so your interfaces stay accessible to colorblind users, and I argue color should support information rather than carry it alone. I show why relying on green/red alone fails for red-green color blindness, then walk through practical fixes: pair color with icons and symbols, differentiate links with underlines or other non-color cues, and develop the whole UI in grayscale before adding color. My take is that designing without color as the primary signal forces you to use spacing, borders, textures, and icons, which makes the interface more usable and often more beautiful.</video:description>
<video:player_loc>https://www.youtube.com/embed/mAKvXGzChns</video:player_loc>
<video:duration>520</video:duration>
<video:publication_date>2025-06-28T06:15:59.000Z</video:publication_date>
</video:video>
<lastmod>2025-06-28T06:15:59.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/this-is-how-you-actually-pick-good-fonts-even-as-a-newbie-designer-NGednsLbgfw</loc>
<video:video>
<video:title>This is how you actually pick good fonts (even as a newbie designer)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/NGednsLbgfw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I break down how to pick genuinely good fonts even if you&apos;re not a typography expert, starting with the four major categories — serif, sans-serif, display, and monospace — and matching them to your brand&apos;s personality. I show how to filter Google Fonts, why you should always pick fonts with 10 or more styles so the browser never fakes bold or italic, and how to use X-height and letter spacing to tell whether a font belongs in headlines or body text. My take is that if you&apos;re stuck, sorting by popularity and spying on competitor websites with dev tools gets you 90% of the way there — and over time you&apos;ll develop a real intuition for typography.</video:description>
<video:player_loc>https://www.youtube.com/embed/NGednsLbgfw</video:player_loc>
<video:duration>661</video:duration>
<video:publication_date>2025-06-26T12:58:37.000Z</video:publication_date>
</video:video>
<lastmod>2025-06-26T12:58:37.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-make-your-text-readable-for-the-web-Os3XCJHP5kw</loc>
<video:video>
<video:title>How to make your text readable for the web</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Os3XCJHP5kw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how to make web text readable with a handful of typography rules you can apply in minutes. I walk through limiting lines to 50–75 characters (I sometimes go 80–85, and use two-column layouts for text-heavy desktop pages), scaling line height inversely with text size and directly with line length, and using larger top margins than bottom margins so headings clearly attach to their paragraphs. I also argue left alignment is the best default for almost all text, center alignment works only for a few sentences, and right alignment is mostly reserved for numeric tables or right-to-left languages. Then I rebuild a deliberately terrible article live to show how those rules turn it into a much more pleasant reading experience.</video:description>
<video:player_loc>https://www.youtube.com/embed/Os3XCJHP5kw</video:player_loc>
<video:duration>462</video:duration>
<video:publication_date>2025-06-22T09:04:29.000Z</video:publication_date>
</video:video>
<lastmod>2025-06-22T09:04:29.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/why-and-how-to-create-your-first-design-system-iHdnNem0S-4</loc>
<video:video>
<video:title>Why (and how) to create your first design system</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/iHdnNem0S-4/hqdefault.jpg</video:thumbnail_loc>
<video:description>Building your design tokens before you design anything is how I 10x design speed, eliminate about 80% of low-level decisions, and get consistent, symmetric interfaces. I define design tokens as reusable variables for colors, typography, spacing, borders, shadows, and icons, then argue you should standardize them in CSS or Figma variables up front: colors need brand, semantic, gray, and accent sets with 8–12 shades each; typography needs font families and a sizing scale; spacing and sizing work best on exponential scales, while color can use linear shades. For the rest, I lean on Tailwind’s prebuilt tokens so beginners can start fast. My take: treat a design system as a spectrum, not all-or-nothing, and create as many tokens as possible before designing to avoid arbitrary one-off values.</video:description>
<video:player_loc>https://www.youtube.com/embed/iHdnNem0S-4</video:player_loc>
<video:duration>872</video:duration>
<video:publication_date>2025-06-10T18:17:03.000Z</video:publication_date>
</video:video>
<lastmod>2025-06-10T18:17:03.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-write-ai-prompts-that-actually-work-step-by-step-NSC0AJgZVCM</loc>
<video:video>
<video:title>How to Write AI Prompts That Actually Work (Step-by-Step)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/NSC0AJgZVCM/hqdefault.jpg</video:thumbnail_loc>
<video:description>I explain how to write AI prompts that actually work by using a step-by-step framework: define the task, break it into beginner-level instructions, add example inputs and outputs, specify the exact output format, and iterate. Most people type two sentences into ChatGPT and expect AGI-level results, but that leaves the model filling in massive context gaps; my own prompts are often a couple thousand words long with explicit steps and examples. I walk through a simple process and demo a prompt for generating viral video titles from a PDF script, including a role, 15 principles, and a strict rule to return only 10 titles ordered best to worst. My take: current models are good enough that a well-written prompt can automate almost any task, but only if you put in the work.</video:description>
<video:player_loc>https://www.youtube.com/embed/NSC0AJgZVCM</video:player_loc>
<video:duration>396</video:duration>
<video:publication_date>2025-05-30T09:38:50.000Z</video:publication_date>
</video:video>
<lastmod>2025-05-30T09:38:50.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/css-dynamic-grid-layout-with-auto-fit-min-and-max-GjRpjZ8cS_0</loc>
<video:video>
<video:title>CSS Dynamic Grid Layout with auto fit, min and max</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/GjRpjZ8cS_0/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a fully responsive CSS Grid card layout with almost zero media queries, using repeat(auto-fit, minmax(min(100%, max(300px, 100% / 4)), 1fr)) to let the browser choose columns automatically. I show why the inner max(300px, 100% / 4) is divided by 4 instead of 3—three columns are needed for the layout, and the extra division accounts for gaps—and why wrapping it in min(100%, …) prevents overflow on small screens. The only media query makes the first and last cards span two columns at the md breakpoint and above. My take is that the math is easier to trust once you recreate it, resize it in devtools, and tweak the values yourself.</video:description>
<video:player_loc>https://www.youtube.com/embed/GjRpjZ8cS_0</video:player_loc>
<video:duration>721</video:duration>
<video:publication_date>2025-05-29T07:57:52.000Z</video:publication_date>
</video:video>
<lastmod>2025-05-29T07:57:52.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/empty-state-uis-why-are-they-important-how-do-you-build-them-XYrHqoXgLCI</loc>
<video:video>
<video:title>Empty-state UIs - Why are they important, how do you build them</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/XYrHqoXgLCI/hqdefault.jpg</video:thumbnail_loc>
<video:description>Empty-state UIs are the screens users see when there’s no content yet, and I show how I design them so a new user never lands on a dead end. I use a job board, notifications, messages, search results, and cloud storage as examples, and I argue that most designers and developers assume content will always exist — until an API returns an empty list and the UI renders nothing. My pattern is simple: describe the problem, explain the next step, and give a clear CTA; illustrations and icons are just polish. The search empty state matters to about 90% of websites, and the same approach keeps every empty screen useful instead of blank.</video:description>
<video:player_loc>https://www.youtube.com/embed/XYrHqoXgLCI</video:player_loc>
<video:duration>841</video:duration>
<video:publication_date>2025-04-23T15:46:33.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-23T15:46:33.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-clamp-css-function-and-responsive-typography-Q0GRrCDIo60</loc>
<video:video>
<video:title>The clamp() CSS function and Responsive Typography</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Q0GRrCDIo60/hqdefault.jpg</video:thumbnail_loc>
<video:description>I show how the CSS clamp() function replaces piles of media queries with one line of responsive typography. I start with font-size controlled by several breakpoints, then swap them for clamp(min, preferred, max) using values like 2.25rem, 1.35rem + 3vw, and 3.75rem. I explain why vw alone fails: it has no min/max and scales too fast, and why the preferred value needs a rem component so browser zoom and accessibility still work. I also demonstrate using a clamp calculator to pick the growth rate from minimum and maximum font sizes and viewport ranges, and my take is that clamp is now the standard way to make headings, body text, and small text responsive without media queries.</video:description>
<video:player_loc>https://www.youtube.com/embed/Q0GRrCDIo60</video:player_loc>
<video:duration>887</video:duration>
<video:publication_date>2025-04-21T16:33:25.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-21T16:33:25.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-13-final-part-noXwR7DGlfU</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 13 (final part)</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/noXwR7DGlfU/hqdefault.jpg</video:thumbnail_loc>
<video:description>I finish the travel agency landing page by making the social proof, CTA, and footer sections fully responsive in Next.js and Tailwind. I use flex-wrap, responsive gaps, min/max widths, and centered justification to keep the social logos from overflowing, then rebuild the CTA around a single absolutely positioned background image with cover, center, mix-blend-multiply, and reduced opacity so the text stays readable. I switch the footer from grid to flexbox with basis-0 and grow values, constrain the input field, fix the icon attribute names, and confirm the whole site looks complete and works across screen sizes.</video:description>
<video:player_loc>https://www.youtube.com/embed/noXwR7DGlfU</video:player_loc>
<video:duration>462</video:duration>
<video:publication_date>2025-04-20T15:26:16.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-20T15:26:16.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-12-Bv6JshbhiiE</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 12</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/Bv6JshbhiiE/hqdefault.jpg</video:thumbnail_loc>
<video:description>I finish making my Next.js and Tailwind travel agency landing page fully responsive, working through the offers, more offers, scrolling offers, products, and reviews sections. I replace rigid grids with flex-wrap layouts, tune min-widths and breakpoints, use clamp() for responsive text, and make the review slider accessible with a pause/play button instead of relying on hover. My take is that responsive design is mostly about controlled collapse: decide what hides, what wraps, and what stays usable on mobile, then verify each breakpoint.</video:description>
<video:player_loc>https://www.youtube.com/embed/Bv6JshbhiiE</video:player_loc>
<video:duration>1138</video:duration>
<video:publication_date>2025-04-19T17:21:06.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-19T17:21:06.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-11-IM7fntfyljU</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 11</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/IM7fntfyljU/hqdefault.jpg</video:thumbnail_loc>
<video:description>I make the Travel Buddy travel-agency landing page responsive with Next.js and Tailwind, starting from the broken desktop-only build and turning it into a mobile-first layout. I add a 480px extra-small breakpoint, container padding, and responsive rules to the super header and navbar, including a client-side mobile menu toggle. I use clamp() for fluid hero and section headings, rework the benefits grid from one column on mobile to three on desktop, and use container queries plus flex wrapping for the destinations section and arrow controls. By the end, the same components hold up from small phones through large desktop screens without separate layouts.</video:description>
<video:player_loc>https://www.youtube.com/embed/IM7fntfyljU</video:player_loc>
<video:duration>1183</video:duration>
<video:publication_date>2025-04-17T16:42:30.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-17T16:42:30.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-10-waO-yVNRUSA</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 10</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/waO-yVNRUSA/hqdefault.jpg</video:thumbnail_loc>
<video:description>I finish the travel-agency landing page by building a dark slate footer with a newsletter signup, social links, quick-link columns, copyright, and a back-to-top button in NextJS and Tailwind. I reuse the PrimaryButton component from the navbar for the subscribe action, pull social and arrow icons from Bootstrap Icons and Material Symbols, and arrange the content with a four-column grid where the brand/newsletter block spans two columns. I also tune the divider to slate-700, add hover transitions on social links, and keep the layout simple before making the whole page responsive in the next step.</video:description>
<video:player_loc>https://www.youtube.com/embed/waO-yVNRUSA</video:player_loc>
<video:duration>1048</video:duration>
<video:publication_date>2025-04-16T07:13:38.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-16T07:13:38.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-9-PoKMKNat1Cw</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 9</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/PoKMKNat1Cw/hqdefault.jpg</video:thumbnail_loc>
<video:description>I finish the travel-agency landing page by building a dark slate footer with a newsletter signup, social links, quick-link columns, copyright, and a back-to-top button in NextJS and Tailwind. I reuse the PrimaryButton component from the navbar for the subscribe action, pull social and arrow icons from Bootstrap Icons and Material Symbols, and arrange the content with a four-column grid where the brand/newsletter block spans two columns. I also tune the divider to slate-700, add hover transitions on social links, and keep the layout simple before making the whole page responsive in the next step.</video:description>
<video:player_loc>https://www.youtube.com/embed/PoKMKNat1Cw</video:player_loc>
<video:duration>450</video:duration>
<video:publication_date>2025-04-12T16:54:12.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-12T16:54:12.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-8-VIFNtsjp4j8</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 8</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/VIFNtsjp4j8/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the social section of my Next.js and Tailwind travel agency landing page, turning four destination photos into a hover-reveal grid with Instagram labels and an “Explore more” button. I create a reusable Social component with an orange-50 section, container, “Social” label, and brand-colored section heading, then map a posts array containing image, location, and icon. Each image sits in a relative overflow-hidden rounded wrapper; on group hover the white overlay fades from 0 to 80% over 300ms, the image scales up, and the button appears. It’s a short, focused build that keeps the layout responsive with flex, basis-0, and grow.</video:description>
<video:player_loc>https://www.youtube.com/embed/VIFNtsjp4j8</video:player_loc>
<video:duration>426</video:duration>
<video:publication_date>2025-04-11T16:02:07.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-11T16:02:07.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-7-bhcD8P47_68</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 7</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/bhcD8P47_68/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the animated customer reviews section of my Next.js and Tailwind travel agency landing page by extracting the scrolling marquee I already used for the office section into a reusable Slider component. I generate six travel-agency testimonials with GPT as JSON, then render them in cards with randomized colors, centered quotes, and alternating top and bottom margins so the duplicated lists stay aligned. I finish by stretching the cards full width and adding left and right gradient overlays so the reviews fade into the background.</video:description>
<video:player_loc>https://www.youtube.com/embed/bhcD8P47_68</video:player_loc>
<video:duration>714</video:duration>
<video:publication_date>2025-04-10T14:04:16.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-10T14:04:16.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-6-cn2_2YHsKp4</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 6</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/cn2_2YHsKp4/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the Featured Travel Essentials product grid for my travel agency landing page with Next.js and Tailwind CSS. I create a new products.tsx section, reuse my heading label component, and render four products — Bags, Tools, Camping Gear, and Suitcases — from an array with map, using stock images from Freepik. I style each card with rounded borders, flex/grow sizing, object-contain and object-center images, centered bold uppercase titles, and a group hover effect that scales the image to 115% using Tailwind v4 arbitrary values, duration-300, and overflow hidden. I finish the section with more padding, an orange-50 background, and top margin on the product list.</video:description>
<video:player_loc>https://www.youtube.com/embed/cn2_2YHsKp4</video:player_loc>
<video:duration>391</video:duration>
<video:publication_date>2025-04-09T15:14:17.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-09T15:14:17.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-5-VBEL1UBDovQ</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 5</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/VBEL1UBDovQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a horizontal sliding animated offers section for a travel agency landing page with Next.js and Tailwind CSS, using a reusable component and icon-based offer data. I show how to render duplicate offer lists inside an overflow-hidden parent so the slide loops seamlessly, add a translateX keyframe animation in globals.css, and tune it to a 9-second linear infinite loop that pauses on hover. My take is that small details like whitespace-nowrap, camelCase icon exports, and screen-reader labels are what keep this kind of marquee clean and accessible.</video:description>
<video:player_loc>https://www.youtube.com/embed/VBEL1UBDovQ</video:player_loc>
<video:duration>810</video:duration>
<video:publication_date>2025-04-08T16:31:30.000Z</video:publication_date>
</video:video>
<lastmod>2025-04-08T16:31:30.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-4-LhUfxYepNsI</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 4</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/LhUfxYepNsI/hqdefault.jpg</video:thumbnail_loc>
<video:description>I continue building the travel agency landing page with Next.js and Tailwind, adding a special offers section and a multi-card more offers section. I fix an icon casing error, extract a reusable SectionHeading and WhiteExternalButton, and give the button an optional bordered prop so it looks right in both the hero and offers areas. The first offers block becomes a two-column grid with an image, a floating testimonial card, and a CTA. For the second block, I use a three-column grid where the first offer spans two columns, reuse the offer-card markup, add stock images, and settle on a percentage icon for discounts after searching 14,000 icons and not finding a decent one. The result is two polished, reusable offer sections that match the site&apos;s orange theme.</video:description>
<video:player_loc>https://www.youtube.com/embed/LhUfxYepNsI</video:player_loc>
<video:duration>1136</video:duration>
<video:publication_date>2025-03-28T09:01:26.000Z</video:publication_date>
</video:video>
<lastmod>2025-03-28T09:01:26.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-3-TMfeLbjMdn8</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 3</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/TMfeLbjMdn8/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the featured destinations section of a travel agency landing page with Next.js and Tailwind, covering the card grid, pricing overlays, carousel arrows, and final background treatment. I create a FeaturedDestinations component, reuse the heading label with inline-block, generate placeholder destination data as JSON with ChatGPT, and swap in real Unsplash/Pexels images. I then render a three-column card grid with aspect-square images, location icons, Explore More buttons, unique card colors, and an absolute price badge, before finishing with circular back/forward controls and a stock background image under a white gradient overlay.</video:description>
<video:player_loc>https://www.youtube.com/embed/TMfeLbjMdn8</video:player_loc>
<video:duration>1007</video:duration>
<video:publication_date>2025-03-27T08:49:30.000Z</video:publication_date>
</video:video>
<lastmod>2025-03-27T08:49:30.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-2-IcWi7vNvKgQ</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 2</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/IcWi7vNvKgQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the benefits section of a travel agency landing page with Next.js and Tailwind, turning a badge, headline, four icon benefits, and a central stock image into the final layout. I create reusable HeadingLabel and Benefit components, type the benefit props, render the benefits from an array, and split them into two groups around the image. I then use a three-column grid with flex columns and gaps to match the target design, using slate-800 instead of pure black and adding screen-reader labels to icons.</video:description>
<video:player_loc>https://www.youtube.com/embed/IcWi7vNvKgQ</video:player_loc>
<video:duration>768</video:duration>
<video:publication_date>2025-03-21T08:40:52.000Z</video:publication_date>
</video:video>
<lastmod>2025-03-21T08:40:52.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/landing-page-design-tutorial-nextjs-tailwind-travel-agency-part-1-P1yFHAJsB-M</loc>
<video:video>
<video:title>Landing Page Design Tutorial - NextJS &amp; Tailwind (Travel Agency) - Part 1</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/P1yFHAJsB-M/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build the hero section of a travel-agency landing page with Next.js, TypeScript, and Tailwind, starting from a blank create-next-app and finishing with a styled, accessible hero. I set up reusable Container and Icons components, build the super header and navbar with screen-reader labels, then layer a full-bleed hero image, a Spotify-style gradient overlay, a headline, subheadline, and CTAs. I show how to fix the overlay blocking text selection with z-index and isolate, use Tailwind v4 opacity classes, and wire up the Poppins font through Tailwind. My take: small structural pieces—semantic HTML, containers, accessible icons, and careful layering—make the design easier to extend in later parts.</video:description>
<video:player_loc>https://www.youtube.com/embed/P1yFHAJsB-M</video:player_loc>
<video:duration>1943</video:duration>
<video:publication_date>2025-03-20T11:20:17.000Z</video:publication_date>
</video:video>
<lastmod>2025-03-20T11:20:17.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/whats-new-in-tailwind-v4-JzMbJ7Os2YY</loc>
<video:video>
<video:title>What&apos;s new in Tailwind V4</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/JzMbJ7Os2YY/hqdefault.jpg</video:thumbnail_loc>
<video:description>Tailwind V4 is a major shift toward zero-config, high-performance styling, and I walk through the changes that actually matter when upgrading from V3. I show the new build numbers — full builds nearly 4× faster, incremental rebuilds with new CSS nearly 9× faster, and rebuilds with no new CSS 182× faster — then cover the removal of tailwind.config.js, automatic content detection, and moving theme configuration into CSS with @theme. I demonstrate how @theme variables become native CSS variables, dynamic utilities for things like 15-column grids and arbitrary spacing, and container queries that make components respond to their container rather than the viewport. My take: the OKLCH palette swap is mostly internal and not something to worry about, and while dynamic values are useful, I still avoid one-off arbitrary values to keep designs consistent. I&apos;m excited to use this in my own projects.</video:description>
<video:player_loc>https://www.youtube.com/embed/JzMbJ7Os2YY</video:player_loc>
<video:duration>743</video:duration>
<video:publication_date>2025-03-19T08:03:02.000Z</video:publication_date>
</video:video>
<lastmod>2025-03-19T08:03:02.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/tailwind-masonry-grid-layout-tutorial-fully-responsive-9AH1EX8nqSk</loc>
<video:video>
<video:title>Tailwind Masonry Grid Layout Tutorial - Fully Responsive</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/9AH1EX8nqSk/hqdefault.jpg</video:thumbnail_loc>
<video:description>I build a fully responsive masonry grid layout in React and Tailwind using the CSS columns property, the same pattern behind Pinterest and Unsplash. I create a reusable Masonry component with TypeScript generics, expose items and renderItem props so you control how every card renders, and add responsive column classes to adapt across breakpoints. The result is a short, reusable component that handles mixed-height content without JavaScript layout calculations.</video:description>
<video:player_loc>https://www.youtube.com/embed/9AH1EX8nqSk</video:player_loc>
<video:duration>366</video:duration>
<video:publication_date>2025-03-18T08:18:21.000Z</video:publication_date>
</video:video>
<lastmod>2025-03-18T08:18:21.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/the-programming-language-or-framework-you-learn-first-doesnt-matter-_jhiTfrNGks</loc>
<video:video>
<video:title>The programming language or framework you learn first doesn&apos;t matter</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/_jhiTfrNGks/hqdefault.jpg</video:thumbnail_loc>
<video:description>The programming language or framework you learn first doesn&apos;t matter nearly as much as becoming a problem solver who can use tools to solve valuable problems. I share how my own Python-vs-JavaScript analysis paralysis wasted weeks, and I argue that programming is really about problem solving: languages, frameworks, AI, WordPress, algorithms, and design patterns are just tools. I show why defining yourself as a React or Python developer limits you, why AI won&apos;t replace problem solvers, and why employers and clients pay for solved problems, not tool labels. My conclusion is simple: don&apos;t chase or identify with every new tool; learn the tools you need, build proof you can solve important problems, and make yourself irreplaceable as a problem solver.</video:description>
<video:player_loc>https://www.youtube.com/embed/_jhiTfrNGks</video:player_loc>
<video:duration>927</video:duration>
<video:publication_date>2025-02-28T09:51:26.000Z</video:publication_date>
</video:video>
<lastmod>2025-02-28T09:51:26.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/i-built-this-cool-little-fitness-app-called-athletix-4cML3Rt3HnQ</loc>
<video:video>
<video:title>I built this cool little fitness app called Athletix</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/4cML3Rt3HnQ/hqdefault.jpg</video:thumbnail_loc>
<video:description>I built Athletix, a full-stack fitness tracker, to sharpen my Next.js skills and practice original UI design instead of only recreating other designers’ work. I show how I log workouts with exercise autocomplete, auto-filled sets from past performance, and set tags for weight PRs and best sets; how I create push-pull-legs templates that repeat weekly and drive a 14-day upcoming-workout view; how measurements can be favorited onto the dashboard and graphed over time; and how exercise stats like estimated one-rep max and max volume are calculated from logged workouts. I also cover React Joyride onboarding, React Table for the exercise library, fitness goals, unit settings, and the Docker Compose setup. I found a bug where the measurement tutorial repeats after adding a record, which I plan to fix. Overall, I built an app I’d actually use and leveled up the skills I set out to learn.</video:description>
<video:player_loc>https://www.youtube.com/embed/4cML3Rt3HnQ</video:player_loc>
<video:duration>980</video:duration>
<video:publication_date>2025-02-25T10:48:16.000Z</video:publication_date>
</video:video>
<lastmod>2025-02-25T10:48:16.000Z</lastmod>
</url>
</urlset>
