<?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-17</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-17</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-17T17:35:48.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/terminal-fundamentals-for-vibe-coders-in-20-minutes-1J6pajNZRvs</loc>
<video:video>
<video:title>Terminal Fundamentals For Vibe Coders In 20 Minutes</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/1J6pajNZRvs/hqdefault.jpg</video:thumbnail_loc>
<video:description>I walk complete beginners through the terminal so they can read an AI coding agent&apos;s commands and decide whether to approve them. I cover the 80/20 Bash commands — mkdir, cd, ls, cp, mv, rm, cat, grep, find and curl — on a real terminal, then flag the patterns that should make you stop: rm -rf, sudo, curl piped into bash, and any command touching paths outside your project folder. I show how to launch an agent from the right project directory, paste an unfamiliar command into an LLM and ask what it does and what the worst case is, and why that only works once you understand the basics yourself. My take is simple: never use dangerously skip permissions on your real machine, because the model lacks your business context and will not ask before reading sensitive files or running something stupid.</video:description>
<video:player_loc>https://www.youtube.com/embed/1J6pajNZRvs</video:player_loc>
<video:duration>1219</video:duration>
<video:publication_date>2026-10-17T17:35:48.000Z</video:publication_date>
</video:video>
<lastmod>2026-10-17T17:35:48.000Z</lastmod>
</url>
<url>
<loc>https://www.hassandev.me/blog/youtube/how-to-pick-the-most-cost-effective-model-for-your-task-j3aqbvLIBd4</loc>
<video:video>
<video:title>How To Pick The Most Cost Effective Model For Your Task</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/j3aqbvLIBd4/hqdefault.jpg</video:thumbnail_loc>
<video:description>Picking a cost-effective model comes down to scoring your prompt&apos;s outputs on your most capable frontier model, then stepping down to cheaper and smaller models until quality drops below your bar. I walk through the exact loop: run the prompt 5–10 times on your best model, score each output out of 100, average the scores, then repeat the same process on progressively cheaper models. Most companies are burning enormous token budgets — Meta reportedly spent $900 million on Claude tokens, and Uber ran through a $3.4 billion annual budget in four months — largely because employees use frontier models to summarize emails and draft PRs. My take is that you stop at the last model that still clears your quality requirement (say 90/100), or step back up one tier, and most business tasks will run fine on small, cheap models. It&apos;s manual spreadsheet work, but it drives costs down hard.</video:description>
<video:player_loc>https://www.youtube.com/embed/j3aqbvLIBd4</video:player_loc>
<video:duration>535</video:duration>
<video:publication_date>2026-10-15T17:30:52.000Z</video:publication_date>
</video:video>
<lastmod>2026-10-15T17:30:52.000Z</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>Learning to code in 2026 still means building real projects and using AI as a code-writing tutor, because I don&apos;t think coding is solved and prompting alone won&apos;t teach you software engineering. I explain why AI makes learning harder when it hides your mistakes, why seniors get a productivity boost while juniors can degrade, and why the best path is still to solve real problems and build useful apps. I use Phantom Editor—19,000 lines of AI-generated code across ffmpeg, browser automation, and social APIs—to show how I paste generated code back into the agent, ask for explanations, and keep questioning until I understand the implementation. I also walk through fixing a slow video-encoding bottleneck by having AI analyze the feature, explaining every suggested optimization, then implementing and testing them. My take is to skip traditional syntax grinding and focus on architecture, performance, security, and system design.</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/my-ai-slop-code-runs-22x-faster-than-yours-IOBacwR4uPA</loc>
<video:video>
<video:title>My AI slop code runs 22x faster than yours</video:title>
<video:thumbnail_loc>https://img.youtube.com/vi/IOBacwR4uPA/hqdefault.jpg</video:thumbnail_loc>
<video:description>AI-generated code runs slow because each abstraction layer trades performance for ease, and I show how I make my own AI-generated Phantom Editor pipeline 22x faster by learning the bottlenecks first and then prompting an agent with specific fixes. I explain why you need to understand load balancing, parallelization, algorithms and data structures—like why a hash-map lookup is instant versus scanning a list—before asking AI to optimize anything. Then I walk through concrete changes: encode video once at the end instead of repeatedly, split audio into 60-second chunks so DeepFilter uses all four CPU cores, trim silence before noise reduction, fire transcription requests concurrently, and standardize FFmpeg parameters. My take: AI won&apos;t optimize by default; you have to identify the problem and direct it.</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 choose what to automate by tracking your work for a week instead of brainstorming ideas. I argue most people automate the wrong tasks for two reasons: the automation takes longer to build than the task ever took, and the task produces no real economic value — programmers are especially prone to both because building is the fun part. I show how to log time blocks in a planner or spreadsheet, then target the tasks that consume the most time, repeat most often, and follow a predictable, manual workflow; those are the low-hanging fruit with the highest ROI.</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>I built a scraper and AI filter that pulls job postings from Reddit subreddits like r/remotepython, where solid remote gigs get posted that never make it to Indeed or LinkedIn. The scraper uses a headful Playwright browser with a logged-in account to slip past Reddit&apos;s bot detection, then runs each post through Gemma 4 — a cheap, small model — to classify whether it&apos;s a real hiring opportunity and return structured JSON. I pass in the title, body, subreddit and first 5–10 comments because commenters often catch scams and AI-generated posts the body alone won&apos;t reveal. The final script lines up qualified jobs so I can click through and apply one after another without digging through Reddit manually, and my take is this lets you skip the oversaturated job boards entirely.</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 hallucinate, make wrong tool calls, need constant babysitting, and burn expensive tokens—and chaining them makes it worse, since at 90% reliability per agent a seven-agent workflow fails about half the time. I show how I build reliable automations instead: I use a coding agent to generate deterministic Python scripts once, run them as many times as I want at near-zero token cost, and call an LLM only for the 10–20% of steps that need real reasoning. My take is that the more determinism you introduce into your workflows, the more reliable, cheaper, and maintainable they become.</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>Applying to jobs is 99% repetitive, deterministic work, so I show how to build a pipeline that scrapes listings from Indeed and LinkedIn, checks each one against your resume with an LLM, tailors a new resume, and submits the application automatically—enough to send a thousand hyper-personalized applications a day. I use Playwright for the browser automation, always with a persistent logged-in account to slip past bot detection, and role/accessibility selectors instead of fragile CSS ones. I hardcode deterministic answers like salary, location, and years of experience to save tokens, and feed a common Q&amp;A document to the LLM only for open-ended questions. My take: most people overuse LLMs for this; keep every deterministic step in code and reserve the model for the fit check and the open-ended stuff.</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&apos;s $850 billion valuation sits on $6.7 billion in revenue against $12 billion in losses, Anthropic&apos;s near-trillion valuation is just as disconnected from profit, and neither will ever recover its investment once Chinese and open source models keep commoditizing the product and the subsidies end. But the technology isn&apos;t going anywhere — nobody&apos;s going back to manual coding, and Jevons Paradox explains why rising developer productivity increases demand for code and likely grows jobs rather than killing them. My take: the labs may go bankrupt, but if you&apos;re not using AI to write code, you&apos;ll get left behind.</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 think traditional GUIs are becoming obsolete as AI and automation handle more of our work. I show how I&apos;ve automated my video editing, lead scraping, job applications, and cold outreach pipelines — now I just run a process command or script and review the output, barely touching the actual software. My take is that most of this doesn&apos;t even require AI; deterministic automations can go a long way. If you want to keep up, stop doing things manually that you could automate, and shift to reviewing outputs instead.</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 tool that finds outperforming YouTube videos, titles, and thumbnails by scoring each video&apos;s views, likes, and engagement against its channel&apos;s average. I walk through the outlier scoring, time-range and multiplier filters, channel tracking, and list-based filtering that let me surface only the best-performing ideas in a niche. I also demo a thumbnail previewer that tests designs at real YouTube sizes and layouts, plus Gemini-powered video summaries, generated chapters, and comment sentiment analysis; the sentiment feature was too slow on Google&apos;s overloaded servers, so I plan to switch providers. My take is simple: this is content research you can run yourself for free, with no signups, and I&apos;m still adding features as I need them.</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 installing software is a clear case where deterministic code beats an LLM. I break down a GitHub project that tells you to point an AI agent at a markdown file to install its SQL interface, when the whole thing is just five or six bash commands — curl the release, clone the repo, build with Gradle and CMake. LLMs are non-deterministic, so that install prompt can behave differently every time while burning expensive tokens, whereas a script runs the exact same way for free. I compare it to the bun.sh install script — still custom code you should review, but at least it&apos;s deterministic. My take: code and LLMs solve different problems, so use code for deterministic actions like installation and LLMs for reasoning tasks, and stop falling for the hype that everything needs an AI agent.</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 routing every video file through a Cloudflare Worker instead of transferring my domain to Cloudflare, then cleaning up the player UI and adding real view tracking. The workaround is a roughly 70-line Worker bound to my R2 bucket that proxies file requests, so I keep Vercel for the app and avoid the rate-limited r2.dev domain. I log page views, plays and completions into a Google Sheet over OAuth rather than standing up Postgres, generate timestamped chapters as a VTT file through OpenRouter (Gemma 4 first, then Nemotron 3 Ultra after Google&apos;s rate limits), transcribe with Groq Whisper, and fold compression, transcription, chapter generation and upload into one script so files land in R2 with matching names. I deploy to Vercel with a custom domain, and the whole build takes two to three hours.</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 for sending custom cold-outreach video messages, using Next.js, a Cloudflare R2 bucket, rclone/ffmpeg upload scripts, and Vidstack. I reject YouTube unlisted and Google Drive because they look unprofessional and don&apos;t scale, then set up R2 with a budget alert, configure rclone, and write a script that losslessly compresses and uploads videos. I skip a database by putting the original filename plus a UUID in the R2 object key and using that key in the video URL. I hit CORS and custom-domain/CDN roadblocks, fall back to the R2 public dev URL, and get the first successful Vidstack playback. My take: iterate cheaply with AI agents, but make deliberate architecture calls. Part two fixes CORS, design, and Cloudflare CDN with a custom domain.</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 generated $430,000 CAD in previously spam-trapped quotations for Master Sealer by fixing their email deliverability and automating their quoting and invoicing workflow. I built a custom Django web app that lets the CEO enter customer info once and then generates PDFs, sends emails, and tracks status automatically, saving 10+ hours a week. I migrated them to Google Workspace and repaired SPF, DKIM, and DMARC records, which improved deliverability by 50.61% and moved quotations from spam to inboxes. I also moved their internal, seasonal app from an always-on AWS EC2 instance to Vercel&apos;s free tier, saving $500 a year, worked around serverless PDF-generation limits, and patched DNS security issues. My take: these unglamorous technical fixes directly create revenue, and every business needs a dedicated technical owner instead of ticket-roulette support.</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>Every automation that touches sensitive or permanent actions needs a human approval step before it runs unsupervised. I show why with real horror stories: an autonomous agent that deleted a company&apos;s entire database and backups after hitting a routine credential mismatch, and OpenAI&apos;s GPT model hacking Hugging Face during a cybersecurity benchmark because the eval was too hard — it went and stole the answers from a real site. Anthropic did the same to three websites during their own evaluations. The fix is simple: let agents handle non-sensitive work autonomously, but require sign-off before anything permanent like database changes or messages you can&apos;t undo. Even though I haven&apos;t had an agent suggest anything catastrophic yet, I&apos;m not removing the review step.</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 your support department by building a Salesforce-style AI support agent for your website, starting from a knowledge base and ending with a deployed RAG pipeline. I walk through why your support articles are the prerequisite, how to craft a natural company voice instead of a generic chatbot voice, and how RAG retrieves your docs, augments the model, and generates step-by-step answers. My take is that support is cheap to automate with smaller open-source models because it does not need frontier reasoning. I also cover bonus AI workflows for categorizing tickets, scoring customer satisfaction, and spotting recurring pain points, and I land on deploying the agent once the pipeline is working.</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 build every AI automation and agent with detailed logs and immediate alerts so I’m never operating in the dark. I argue that automations inevitably break—LLMs hallucinate invalid output, scrapers get blocked or hit changed HTML, GUI automations break when screen sizes shift—so the goal is not preventing every failure but making failures easy to retrace and fix. I show how logging every step and alerting the decision maker immediately prevents the scenario I’ve seen where an automation crashes and nobody notices for weeks; my take is that proper logs and precise alerts are the most important operational layer for any AI workflow or agent.</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>Reliable automations beat fancy AI agents, and I show how to build them by maximizing deterministic code and using LLMs only where real reasoning is required. Most people asking for an AI agent do not need one: LLMs hallucinate, need constant babysitting, and burn tokens, while many so-called reasoning tasks are just if-else logic in disguise. I walk through writing the process down step by step, skipping agent architecture, using a coding agent like Codex or Claude Code to write the deterministic code, and then iterating to replace LLM steps with traditional code as the workflow becomes clear. My browser-automation example: let Claude discover the workflow once, then rewrite it in Playwright or Puppeteer; scrape deterministically, then let an agent analyze the saved data. I land on adding as much determinism as possible and reducing reliance on LLMs.</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 all-or-nothing: automating 80% of a task is far better than automating none of it. I argue that every task has routine, repetitive elements you can automate with traditional code, and you can use LLM calls only when reasoning or decisions are needed to get 80–90% of the way there. I use the math: if you spend 10 hours a week on video editing or social media management, automating 80% gives you eight hours back, and employees get the same savings. I land on keeping a human reviewer for the final step, because even “autonomous” agents still need review—and that partial automation is more reliable and cheaper than chasing 100%.</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>I get reliable, fast, cheap automations by describing the process at the most granular level possible before building anything. I show how my auto-editor started as tiny chained automations — audio normalization and noise reduction, silence trimming with Silero VAD and Whisper, 4K-to-1080p downscaling — each added after I ran the step manually enough times to understand it. My take: if you can&apos;t explain every tiny step precisely, you don&apos;t understand the process well enough to automate it.</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>I argue you should never automate a task until you’ve standardized it with written SOPs and done it manually at least once or twice, because if you can’t do it by hand you won’t be able to automate it reliably. I show how I automated my video editing pipeline only after doing audio processing, noise reduction, trimming, and bad-take removal manually for dozens of videos, then built it up one small shell-script step at a time. I also use thumbnail automation as the counterexample: without a clear, repeatable process, I can’t automate it yet.</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>I show how to set up autonomous AI agents that read your customer reviews, support tickets and social media comments and turn them into short, actionable reports, so you never have to read hundreds of messages yourself. The setup is simple: pull the data from an API, give the model a precise system prompt describing the analysis and report you want, and have it deliver that report to email or WhatsApp on a daily or weekly schedule. I focus on the negative sources on purpose, because satisfied customers rarely take the time to write anything while frustrated ones complain everywhere, so complaints carry most of the signal. My take is that an agent surfacing your most common pain points lets you fix them in your product, documentation or marketing, which improves the product and cuts support volume at the same time — a win for both you and the customer. It takes under an hour and no technical knowledge.</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 a far more capable coding model than its reputation suggests, and I make the case for using it on complex projects even though OpenAI and Anthropic lead the frontier. I point to a fully automated video editor built from Python scripts and a Next.js/FastAPI app called Discover, both written entirely with Gemini, to argue that the “Gemini can’t code” claim is false. My take is that for 99% of apps Gemini is more than enough, and most failures come from poor prompting and poor context management rather than the model itself. Learn how the systems work, write better prompts, and start fresh chats per feature instead of cramming ten features into one context.</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 off a single Postgres write instance, and the playbook their engineers built around it is the most useful scaling material I&apos;ve read as a backend developer. I walk through every failure mode they had to solve — overload, cache-miss spikes, retry storms, noisy-neighbor workloads, connection exhaustion — and the concrete fix for each: offloading reads to replicas, sharding heavy writes to Azure Cosmos DB, keeping a continuously synced replacement write database, running roughly 50 read replicas with more than one per region, cascading replication so the primary isn&apos;t fanning WAL records out to all of them, PgBouncer pods in Kubernetes, a lock so only one query refills the cache on a miss, rate limits at every layer, and schema changes so strict some take over a week. My take is that this is gold for anyone who wants to scale past a few thousand users without reaching for a new database.</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 YouTube, Instagram, TikTok, X, LinkedIn and other platforms for free using each platform’s API and an AI-generated setup. You copy my long prompt into Claude or ChatGPT, answer its questions, create a developer app to grab the client ID, secret and tokens, and let the LLM write all the code that posts your content. My take is that manual posting and paying $20–$30 a month to Buffer or similar schedulers is unnecessary—once this is set up, the machines handle the repetitive work while you focus on higher-leverage business tasks.</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—not because of a malicious prompt, but by finding a zero-day, escaping its sandbox, gaining internet access, escalating privileges, stealing credentials, chaining exploits, and pulling answers from production infrastructure. My take is that this is the real safety problem: you don&apos;t need an evil model, just a very capable and autonomous one pursuing a simple objective in an unexpected way. I argue we should keep developing AI, but stop treating incidents like this as accidents and start taking capability-driven risk seriously.</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 businesses overspend on LLM tokens because they route every request to a frontier model, so I show how a model router cuts AI agent costs by up to 80%. I walk through a four-step build: sample at least 500 real user prompts and manually categorize them by complexity and task type, create three tiers (simple, standard, frontier), use a small cheap LLM as the router instead of brittle keyword rules, then track costs and quality with human review, LLM-as-judge, escalation, and escalation-rate metrics. The core finding: over 50% of business tasks — formatting, data extraction, classification, summarization, simple Q&amp;A — run fine on cheap models like Qwen 3.6 27B or Gemini 4, and paying frontier prices for them is pure waste.</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 a whole job like social media manager or video editor, break it into granular actions and automate those one at a time. I show how I use this approach myself—my content gets edited and posted automatically, much of my client code is AI-generated, and I save 20–30 hours a week—but I also argue automation still takes real maintenance and won&apos;t magically solve everything or excuse producing slop. My take is to start with one repetitive task in free time, finish it, then move to the next.</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 explain how to automate 80–90% of business work by shifting from role-based thinking to workflow-based thinking and breaking each role into granular, automatable tasks. I walk through video editing, social media management, and customer support, showing how tasks like audio cleanup, captioning, posting, reporting, ticket routing, and review collection can be automated one by one. My take is that you can’t replace every employee with a robot army, but you can automate most of a person’s work. I argue for standardizing processes first, going as granular as possible, preferring reliable deterministic scripts over fancy LLM agents, logging and alerting on failures, and keeping human approval for sensitive tasks. Start by automating one portion of the business, not everything at once.</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 can write code, but building good software still requires ten fundamentals, and I break down the ones that matter now that syntax is automated: databases, data structures and algorithms, system design, API design and security, CI/CD, caching, messaging, and Docker/Kubernetes. I argue the reason the best AI apps still come from experienced developers is that code generation doesn’t cover query optimization, scalability, reliability, secure APIs, service communication, or deployment. I show where each skill fits and what problems it solves—relational vs. vector databases, hash-map lookups vs. array scans, rate limits, Redis caching, message brokers over direct service calls, container orchestration—and point to roadmaps for learning them. My conclusion: learn the fundamentals, not just syntax, and you’ll build software that actually ships and scales.</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 automate my entire long-form content workflow with Phantom Editor, a set of deterministic Python and Bash scripts that handle audio cleanup, silence removal, webcam overlays, captions, music, YouTube uploads, and Twitter sharing. I run through the seven-step pipeline on a real video: normalize and noise-reduce audio, trim silences with Faster Whisper timestamps and FFmpeg, insert a webcam mask via spoken “webcam start/stop” commands, add captions with long/short presets, mix background music at 5%, create metadata, then upload and tweet the link. The silence pass cut a 21-minute recording to 12:14, and I estimate the whole system saves me 15–20 hours a week. I also show the order mistake I made—attaching the webcam before trimming silences forced a second trim—and argue that creators should automate repetitive work with simple, reliable scripts rather than LLM agents that can hallucinate.</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>The programming industry has become a slop factory, and I&apos;m tired of pretending otherwise. I point to Spotify&apos;s VP of engineering bragging about 4,500 production deployments per day when a heavy user like me notices zero new features, zero bug fixes, zero performance gains—and ask what business value that represents. I call out AI grifters like Peter Steinberger telling people to design endless token-burning agentic loops while being sponsored by OpenAI, and I argue that the whole industry is stuck sharpening the axe: building better tools and harnesses to produce code faster instead of using those tools to build real software people actually want. GitHub and AWS go down constantly now, Uber burned $3.4 billion on tokens with nothing to show for it, and Microsoft keeps shoving useless Copilot into Windows. My take is simple: stop indulging in slop for production, build high-quality software that genuinely solves problems, and don&apos;t listen to grifters who don&apos;t know what they&apos;re talking about.</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 to caption short-form videos for free using a local Faster Whisper model to generate subtitles and FFmpeg to burn styled captions directly into the final video. I walk through the simple pipeline — raw video in, transcript generated, captions burned at the right timestamps with your chosen font, size, placement, background, and shadows, final file saved — and explain why I use the small or medium model for regular English while reserving large-v3 for multilingual or maximum-quality work despite its speed and RAM cost. I also show the 400-line script I vibe-coded for this, which I never reviewed because it is a local, non-production tool, and argue you can build the same thing yourself once you understand the process and prompt your coding agent with the details.</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>LLMs and AI coding agents still can’t automate skilled engineering work, and I use the Ghostty creator’s renderer-optimization experiment to show why human expertise remains the bottleneck. His agent ran for four hours and spent about $350 to take a naive renderer from 88 ms and 150,000 allocations per frame down to 1 ms and 500 allocations—but his hand-written renderer hit 20 microseconds with zero allocations. My take is that this is the same flaw I’ve argued before: if you don’t understand systems and coding yourself, you can’t guide the agent properly, spot far better solutions, or resist output that sounds confident but is wrong. Keep learning and building—no matter how good the models get, they still need you at the steering wheel.</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 LLMs and AI agents are driving us toward cognitive surrender—a state where we stop thinking critically because the machine makes decisions for us—and that this is already visible in software development. I show how jumping straight to the prompt box skips the problem-solving process, why manually reviewing thousands of lines of AI-generated code is a doomed safeguard nobody will follow, and how the agent&apos;s confidence tricks us into accepting broken output. My take is that as models improve, we keep abstracting away our own skills until we&apos;ve lost them entirely, leaving us helpless when the bot fails—and the only real winners are the AI labs collecting billions in token revenue while companies like Uber spend $3.4 billion in four months and have nothing to show for it.</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 minimal jump rope tracker app with an AI coach that reads your workout data, goals and streaks through tool calls to give personalized insights and automatic weekly digests. I walk through the features — simple workout logging with auto-calculated skips per minute, goal tracking, streaks, share cards, and offline-first SQLite storage with optional cloud sync — and break down my stack: React Native and Expo, a FastAPI proxy on Vercel, and Google Gemini powering the AI. My take is that AI-assisted coding is the future: I wrote zero code by hand, directing the AI with context and feature specs instead, and the entire app is open source. I also explain why I built it: one tool that does one thing well, in the spirit of the Unix philosophy.</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 I argue that the harness around the model — its UX, the context it gets, and the tools it can use — matters far more than which model you pick. I show why benchmarks are misleading (benchmark maxing plus no real-world signal), why a 12B open-source model with a good harness can outperform a frontier model without one, and why Cursor beats rivals despite using the same underlying models. My take is that for most business tasks, expensive frontier models are a waste — DeepSeek and Llama get the job done at a fraction of the cost. I close with practical harness-engineering advice: give agents tools to act and have them pull context automatically.</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 review the Karatas ecommerce store and show why its biggest conversion problem is that customers have no way to buy anything: products have no prices, no add-to-cart or buy-now option, and no checkout, only a WhatsApp quote request that introduces delay when leads are already ready to pay. I argue the homepage overemphasizes the company story and barely shows the actual products, even though the design, branding, responsiveness and page-speed basics are solid. My conclusion is to fix the product and checkout path first—including pricing the predefined sizes and adding real purchase options—before spending anything on paid ads, because traffic is wasted if there is nothing to buy.</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>I argue that heavy AI use atrophies your critical thinking and coding skills through what I call cognitive surrender: you stop thinking through problems and let the model decide for you. I explain why human review guardrails fail in practice — when AI output is right even 90% of the time, people cognitively check out and stop catching subtle bugs or hallucinations — and why autonomous agents without human reasoning just deepen that surrender. I also show how automating routine cognitive work removes the daily practice that keeps your skills sharp, using a Vercel vs. AWS deployment choice to illustrate why you still need judgment to evaluate AI recommendations. My conclusion is that because LLMs hallucinate, you always need the expertise to review their output, and senior developers get the best results precisely because they have those skills.</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>Token maxxing is a failed experiment: ranking engineers by how many LLM tokens they burn instead of what they actually ship destroys budgets and produces nothing. I walk through the numbers — Uber blowing its entire $3.4 billion 2026 budget in four months (roughly $28 million a day), a company that spent $500 million on tokens in a single month because nobody set usage limits, Microsoft canceling Claude Code licenses, Amazon quietly removing its AI leaderboard once costs spiraled, and companies laying people off just to pay their token bills. I argue that raw token counts are a fool&apos;s way to measure AI usage; you should track problems solved, features built, and real productive output instead. Any company still running token maxxing leaderboards is asking to go bankrupt.</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 bare chat box with an LLM behind it, and no real developer can ship production-grade software in it. I go through what&apos;s gone: you only see files changed in the current chat rather than the whole codebase, there&apos;s no integrated terminal, no WSL or SSH, no way to create .env files (so your API keys and secrets go to Gemini as training data), no definitions or references, no search or replace, and no Git — no branches, no undo, no linter errors — even though the VS Code fork it came from already solved all of this. My take is that chat boxes are the wrong interface for real software and the agentic sidebar inside an IDE is the best AI integration we have, so the only sane way to use 2.0 is to hit Open IDE and close that chat window for good.</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>I break down the absurd survival tactics developers are using as AI layoffs hit—voice-dictating all your code with WhisperFlow, automating a coworker&apos;s entire job so you look indispensable, and peppering managers with buzzwords like autonomous agents and vector databases—before pulling the rug: it&apos;s all satire. The real argument is that companies are in a state of AI psychosis, exemplified by Coinbase laying off 14% of its workforce while its CEO suggests vibe-coded slop from sales and HR can replace engineers at a company nation-states are actively trying to hack. I land on a simple point: AI is just fancy autocomplete, a non-deterministic, hallucinatory tool that needs constant babysitting, and pretending otherwise is how you end up shipping vulnerable code and making terrible decisions.</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 only &apos;automates&apos; experts, because it takes an expert to prompt it correctly, supply the right context and steer it to a finished result — and if a human expert still has to run the agent, nothing has been automated at all. Drawing on coding since 2019, I point to vibe coders shipping 6,000-line files and people who can&apos;t start a FastAPI dev server without asking their agent, and contrast them with senior engineers whose output AI multiplies. I also argue the &apos;software engineering is dead&apos; narrative from Anthropic and OpenAI is investor-driven hype, noting Google ships frontier models without the doomerism because it doesn&apos;t need VC money. My conclusion: keep learning — expertise is more valuable now, not less.</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>Token-usage leaderboards are a terrible way to measure employee AI productivity, and I argue companies should stop using them. I point to Meta&apos;s internal token leaderboard, where employees burned throwaway prompts to climb the rankings and roughly $900 million worth of LLM tokens were used in 30 days before the leaderboard was removed, plus Microsoft&apos;s employee AI-usage dashboard and Salesforce&apos;s requirement that every employee spend at least $100 on Claude Code and $70 on Cursor each month or get flagged. Employees respond by generating code they never intend to use just to inflate their numbers. The only real winners are Anthropic, OpenAI and Google; everyone else is burning inference budget on slop, and I&apos;d be happy when this AI bubble pops.</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 the AI training coach in my jump rope tracker as a Gemini-powered agent that can read workout data, goals and streaks and also update goals or log workouts through tools, and I explain the architecture behind it: a FastAPI backend on Vercel handles Gemini API calls while a React Native chat interface lets users ask training questions in natural language. I also built a Weekly Digest that automatically analyzes the week’s logged sessions, extracts insights and writes a short article on performance and how to improve. My take is that this is the first AI agent I’ve built that is actually useful to me, and I chose Gemini because it was the only major provider with a free tier I could use for the 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 Bash and FFmpeg: I extract WAV audio from each MP4, run DeepFilterNet for noise reduction, normalize with FFmpeg, merge the processed audio back into the video, and organize every intermediate file in a temp directory. I show the individual scripts and the single process_audio wrapper that runs the whole chain from one input MP4. My take is that automation starts with precise steps, not tools: if you can describe the process exactly, you can automate it. The pipeline works end to end, and the DeepFilterNet noise reduction sounds better to me than Audacity; on my slow CPU the noise-reduction step takes five to ten minutes, and the strange colored log messages don’t affect the output.</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>Most vibe-coded apps ship with the same handful of security holes, so I walk through the five or six I see most often and how to close them. I cover keeping secrets in environment variables and out of source control, never exposing API keys to the browser (front-end calls should proxy through a backend), rate limiting write endpoints so spam signups and 5GB file downloads don&apos;t wreck your database or bandwidth bill, and validating input on both the frontend and the backend — frontend for speed and fewer requests, backend because anyone can modify what the client sends. My take is that AI coding is here to stay, so the answer isn&apos;t to abandon it but to learn these fundamentals well enough to prompt for them and verify the output.</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 dozens of tools, build AI slop nobody consumes, and one-shot pointless apps instead of using AI to 10x the work that actually matters. I argue that the tool-hopping hype cycle on Tech Twitter is a trap — Perplexity&apos;s entire business model died the moment ChatGPT added Google Search, and OpenClaw is just the newest kid on the block doing what n8n and Zapier already did. Most of the people hyping these tools haven&apos;t shipped real production software; they&apos;re the same people who spent all day tweaking Linux configs instead of getting work done. Fully outsourcing your life to AI is just as dumb, because the model still needs a human reviewing its output, and handing your accounts, tokens and privileges to these tools means one security vulnerability and you&apos;re done. My method is dumb simple: find the single highest-ROI task you could be doing right now and use AI to 10x that — start from a real problem, not a solution looking for one.</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 explain LLMs, RAG, and AI agents in the simplest terms: an LLM is a language model trained on broad data, RAG grounds it in your own real-time data, and an agent is an LLM with tools it calls in a loop until a task is done. I break RAG into retrieval of relevant data, augmentation by injecting that data into the prompt, and generation of a response from the new context. I show how an agent uses ordinary functions like search, file reads and writes, and custom APIs to complete something like writing an SEO-optimized article, then reports back. My take is that these are the fundamental mental models: default LLMs won&apos;t know your specific dataset, RAG fixes that, and agents extend an LLM with tools.</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>I explain why MySQL is declining and how I recommend moving off it, with MariaDB for existing projects and Postgres for new ones. I argue that Oracle keeps MySQL&apos;s open-source repo mostly for show—zero open pull requests versus MariaDB&apos;s 270—while locking key features like vector search and parallel query execution behind the paid enterprise edition, and that MySQL&apos;s commit activity is falling while each release gets slower and more bloated. I cite sysbench comparisons showing MariaDB outperforming MySQL, and my take is simple: migrate existing MySQL projects to MariaDB because it preserves backwards compatibility in 99% of cases, and choose Postgres for anything new.</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 cover the seven practical controls I use to secure file uploads in APIs, from pre-upload validation to signed URLs and rate limiting. I show how to enforce size, MIME-type and extension limits before upload, store files in third-party object storage like S3 or GCS instead of app servers, scan asynchronously for malware and sandbox files, rename files and strip unneeded metadata, serve files through access-controlled expiring signed URLs instead of direct paths, and add logging, monitoring and rate limiting. My take is that client-side validation is useful for fast feedback and reducing server load, but every check must be duplicated on the server because the client can always be modified.</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 won’t replace junior developers who learn the senior engineering practices that make AI coding tools effective; it will replace juniors who use AI to skip learning those skills. I argue that seniors get far more from AI-assisted coding because they already know clean code, DRY, edge-case handling, documentation, and problem decomposition — and those same skills are prompt engineering. I lay out what I’d learn as a junior to become hard to replace: be specific with prompts, handle edge cases, write clean code, keep code DRY, break large problems into smaller ones, and document your work and codebase so you can maintain it and feed better context to the AI. My take is blunt: if you keep doing the same things, you will get replaced; if you use AI to bypass learning core engineering skills, I can’t help you.</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 of how developers actually use AI for coding — from the &apos;please fix this&apos; crowd still judging today&apos;s models by 2023 Copilot, to the agent-maxers chaining sub-agents and Ralph Wiggum loops — and argue you can skip straight to the end. My approach: pick a frontier model like Opus 4.5, explain the feature in plain English, point it at the relevant files, give clear instructions, and let it work. Complicated agent orchestration is a false sense of productivity and a cheap dopamine hit; 99 times out of 100, plain English with the right context ships more. Developers who don&apos;t adapt will get left behind — the time you save on code goes into architecture and product decisions.</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 the meaning behind words instead of the keywords themselves, which is why a query for &apos;fantasy movies&apos; surfaces Lord of the Rings even though the word never appears in the title. The machinery is vectors: embedding models turn text into vectors, similar concepts point in nearly the same direction, and cosine similarity scores and ranks the results. I walk through the full pipeline I use on my own data — clean and chunk text to fit the model&apos;s token limit, generate embeddings, cache them in a vector database like Pinecone, Chroma or pgvector, embed the incoming query with the same model, then sort by similarity score and return the top 10 or 20 results.</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 agents to ship maintainable software instead of one-shotting unmaintainable code. I walk through my six-step workflow: plan the entire project first by going back and forth with the agent to surface edge cases and write the plan to a markdown file, extract repeated instructions into reusable markdown docs so you never repeat yourself, use plan mode for complex features and actually read the implementation plan before letting the agent code, build iteratively with a refactor-and-test step between features to keep technical debt minimal, commit often and branch for every feature and bug fix so you can always rewind, and never trust the AI&apos;s output blindly — verify it with unit and integration tests. My take is that as long as you get the planning right, the actual coding usually goes very well, and this is just coding intelligently with AI, not real vibe coding.</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>The vibe coding era is here to stay, and any developer still writing every line of code by hand is going to get left behind. I break down why Karpathy&apos;s original &apos;vibe coding&apos; advice — ignore the diffs, paste errors blindly, work around bugs — is terrible for real projects, and how elite engineers like DHH and Linus Torvalds now use agentic tools like Anti-Gravity and Claude Code to build real things (the Omarchy Linux distro, Torvalds&apos; audio visualizer). My take is that this should be called supervised collaboration: treat the AI like a junior programmer you guide, architect the system yourself, and review every output. I explain why Opus 4.5, Gemini 3 and GPT 5.2 changed my mind, and how to start — pick any agentic tool, throw serious work at it, and when the output is bad, ask how to prompt better instead of writing AI off.</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 tools are just the next abstraction over code, and I argue that&apos;s a good thing. Ever since Opus 4.5 I let AI write 80–90% of the code in my projects and I only review it for clean structure, reuse and security. I trace the same pattern from binary to C to Python and TypeScript: each layer sacrificed a little performance and gained millions more developers, and 95% of the money I&apos;ve made came from Python and TypeScript. AI won&apos;t kill software development — sloppy apps sink on their own while useful ones ship far faster. But you still have to keep your lower-level skills, because there are moments, like dropping from a Django ORM to raw SQL, when the abstraction isn&apos;t enough and you have to step in and write the code yourself.</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 an entire jump rope tracking app—over 7,000 lines of code—without writing a single line myself, using Google Anti-Gravity and AI prompts, and I show how to do it without producing vibe-coded trash. The core is planning: I wrote a ~600-line product requirements document and a style guide so the AI has rules for features and UI, then built one feature at a time by having the AI draft an implementation plan I reviewed before any code. I also researched third-party packages myself, fed current docs (like Expo&apos;s new FileSystem API) when the model&apos;s knowledge cutoff caused deprecated code, and used a branch-per-feature Git workflow. My take: AI augments developers rather than replacing them; without fundamentals you can&apos;t guide it, and vibe coders can&apos;t build this app fast without frustration.</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 their event-based, bidirectional model with HTTP’s request-response cycle, then show a minimal Flask and native JavaScript demo where the server pushes events without waiting for a request. I argue WebSockets are the right tool for real-time communication—messaging apps, multiplayer games, and live updates from long-running server processes—because either side can emit events and neither must respond. My take is to use WebSockets only when you need that real-time push, keep HTTP for normal request-response flows, and focus on understanding the protocol under the hood rather than memorizing syntax, since AI can write most of the code.</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 wastes bytes on boilerplate, so for performance-critical apps I built a custom binary protocol and benchmarked it against JSON in a multiplayer game server demo. I show a central server broadcasting every player&apos;s state to all connected clients and measure the payload: a single player state costs 311 bytes in JSON versus 104 bytes in my binary format — at 50 players and 60 updates per second, that&apos;s about 0.8 MB/s versus 0.3 MB/s of network traffic. The savings come from dropping the curly brackets, quotes, commas, and repeated field names that JSON sends for every player on every frame. My take: JSON is the right default for REST APIs and web apps, but if you&apos;re building something performance-critical, look into a custom or predefined binary protocol before you default to JSON.</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 Go web server on localhost:8998 to handle Google OAuth callbacks and capture the authorization code from the redirect, so re-authenticating doesn&apos;t dump you on the broken localhost page. I add localhost:8998/google-oauth-redirect to client.json, create a web package with startHTTP and OAuthRedirectHandler, and run the server in a goroutine tied to a cancelable context. I use http.Server with Shutdown instead of ListenAndServe so the server can stop programmatically after Drive connects, with a one-second shutdown timeout. My take is this is a small quality-of-life change, but it gives you a real redirect page you can later turn into a polished HTML flow with a copy button.</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 wire Tailwind v4 straight into Django 6.0 templates — no REST API, no separate front-end — by running Tailwind&apos;s CLI in watch mode to compile a globals.css into a static output.css that my base.html loads through Django&apos;s static tag. I set the project up with uv for the Python environment, initialize an npm package, install Tailwind with Bun, create an app with a templates and static directory, and add a package.json script so the CSS rebuilds on every change while runserver runs in a second terminal. My point is that Node tooling and Python frameworks don&apos;t need an API boundary between them: a watch command plus a static file is enough, and you keep Django templates and everything else you like about Django.</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 DriveSinker&apos;s sync algorithm by replacing the old ignore checks with a single GitIgnore.shouldIgnore call. I refactor the file-counting and folder-upload functions to accept the new IgnorePattern list and GitIgnore struct, convert patterns back to strings only where needed, and handle ignored directories with filepath.SkipDir while ignored files return nil. After testing, the logs show almost every file skipped except the changed log file and the sync file I had just edited, confirming the new parser works with complicated 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 write the shouldIgnore() function for my custom Gitignore parser in Go, deciding whether a given path should be ignored, skipped as a directory, or uploaded. I convert absolute paths to paths relative to the sync root with filepath.Rel, normalize separators with filepath.ToSlash, and match each parsed pattern against either the full relative path or just the base name depending on whether the pattern is rooted. I handle leading, trailing, and middle patterns, apply negation rules, and return a three-state enum (Ignore, IgnoreDir, Allow) so directories can be skipped with filepath.SkipDir instead of walking every file.</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 Google Drive sync tool, replacing the naive line-by-line matching with one that understands the full pattern syntax. I show how to skip empty lines and # comments, then parse negation (!), directory-only trailing slashes, root-anchored leading slashes, and leading, trailing and middle wildcards into an IgnorePattern struct with boolean flags and a double-star mode enum. I also fold the global ignore list from config.json into the same pattern list so a single shouldIgnore check can decide every file during the folder walk. My take is that a switch statement won&apos;t cut it here, because multiple patterns can combine on one line. The parser is finished; wiring it into the upload path comes next.</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 hunt down and fix three small bugs in my Google Drive sync project before building further: the project-exists query was matching any folder in the parent instead of checking the folder name, the project-tree walker was flooding the terminal with debug prints, and the progress bar would divide by zero when there were zero files to upload. I add the missing name condition, comment out the noisy logs, and refactor the bar rendering to treat zero total files as a completed 100% upload. I also verify the sync still uploads new projects, skips sensitive files like client.json and token.json, and correctly skips everything when the project already exists.</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 proper standard library and an npm ecosystem that rewards tiny, fragile dependencies instead of fixing the language. I walk through the left-pad incident and packages like is-even and is-odd — pulling hundreds of thousands of weekly downloads — to show how JavaScript developers reach for dependencies for things a five-minute utility function should handle, which leaves apps exposed when maintainers unpublish. I argue that JavaScript being beginner-friendly is fine, but without a standardized, secure, performant library it pushes beginners toward inconsistent third-party APIs and non-performant apps. My take is that JavaScript and TypeScript developers should learn Go, Rust, C, C#, or Java to see what proper standard libraries and memory management look like, and stop treating npm as the only world.</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>I add ghost-file deletion to my Google Drive syncer so files removed locally are also moved to Drive trash instead of lingering remotely. I collect every local file and folder path during upload, convert the tracker&apos;s remote node map into a remote-path list, and compare the two to mark any remote path with no local match as a ghost. Each ghost node is then moved to Google Drive&apos;s trash with files.update and trashed=true, and I call deleteGhosts at the end of uploadFolder. I test it by uploading ghost.txt, deleting it locally, and rerunning sync: the remote copy disappears while unchanged files are skipped. My take is the new incremental sync algorithm is nearly complete and far more efficient than deleting and re-uploading the whole project.</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 show how to overwrite existing Google Drive files during a project sync instead of uploading duplicate copies. Modified files were already detected via checksum comparison, but the sync path still created new files, so I query Drive by exact file name and parent ID, then call files.update with the existing file ID and the new media while leaving metadata unchanged. I test it by syncing after adding code to sync.go and confirm the Drive copy is updated in place; deleting ghost files is left 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 a missing step in my Go Redis clone&apos;s BGREWRITEAOF flow: commands that arrive during the rewrite are buffered, but I never flushed that buffer back into the AOF file afterward, so those commands could be lost. I add a buffer.WriteTo(aof.f) call, log any buffer-write error without returning early because future AOF records still need to be rerouted to the file, and test the fix with redis-cli by running BGREWRITEAOF while setting new keys.</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 show how to make the Drive syncer skip unchanged files by adding a shouldUpload method that compares local and remote checksums, and I fix a major subfolder bug by keying subfolders on their full paths instead of names. I also map remote subfolders back into the local subfolders map after walking the project tree, so files inside already-uploaded folders can still find their parent IDs. My take is that this completes about 99% of the tracker: folders are skipped when they already exist, files are re-uploaded only when checksums differ, and duplicate subfolder names no longer collide on Google Drive.</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>I build a queue-driven project-tree walker that maps every local file and folder to its Google Drive counterpart so the syncer can tell what changed. I walk each Drive folder, track folder IDs to local paths, handle nextPageToken pagination, and populate a node struct with id, name, mimeType, isFolder, size, modifiedTime, SHA-256 checksum, and parent. I integrate the walker when the Drive project already exists and print the resulting ID-to-local-path mapping for debugging. The mapping works, but duplicate uploads remain until I add a should-upload check and wire it into the sync algorithm next.</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&apos;s writing style is what sets your brand&apos;s perception and triggers emotion in users, so it has to match your brand personality — a bank can&apos;t sound like your drinking buddies and Discord can&apos;t sound like JPMorgan Chase. I break down the two-step process I use to apply this: first study your target audience and brand mission, then adjust your writing style to fit what that audience actually prefers. I walk through real examples like Discord&apos;s casual, friendly voice versus JPMorgan Chase&apos;s professional, corporate tone, and I show how to use AI tools like ChatGPT or Gemini to analyze your existing text and rewrite it in the style you&apos;re aiming for.</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 won&apos;t replace developers, and I break down why the claims coming out of Anthropic — that AI will write 90% of code within months and that we&apos;ll soon stop checking generated code the way we ignore compiler output — don&apos;t hold up. LLMs are non-deterministic by design, so the compiler comparison is a false equivalence: compilers produce identical output every run, the same prompt to an LLM gives you different code each time, and we actually do read compiler output in plenty of cases. I also point out that Anthropic&apos;s CEO predicted in March 2025 that AI would write 90% of code in three to six months — eight months later, it&apos;s nowhere close. AI speeds up coding, but it multiplies technical debt because you&apos;re shipping more code you didn&apos;t write and don&apos;t fully understand, which is part of why software like Windows 11 keeps getting more bloated. My conclusion: augment your 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 fix the upload progress bars so they reach 100% and start building a more efficient Google Drive sync algorithm that skips unchanged files instead of deleting and re-uploading the whole project folder. I double the Go wait group size and call wg.Done from the progress-bar renderer so the program no longer exits before the bars finish, then add a tracker.go with a project-exists check against the Drive API and a SHA-256 checksum helper. I integrate the tracker by commenting out the old delete-previous-folder step and creating or reusing the project folder ID, but because file-level change tracking isn&apos;t built yet, the upload now produces duplicates. My take is that this is the right direction, and the next step is tracking every file change so I only upload modified files and delete removed ones.</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 DriveSyncer upload multiple project folders concurrently and add per-folder terminal progress bars. I move each folder upload into a goroutine guarded by a WaitGroup, stream UploadProgress pointers through a channel, redirect all log output to drivesinker.log so the terminal stays clean, and build renderProgressBar/renderBars utilities that use uploaded/total file counts plus ANSI escape codes to clear and redraw each bar. I also count only files while respecting the ignore list so progress totals match what actually uploads. The result works with multiple projects uploading simultaneously, but I call out a remaining bug: the program can exit before a progress bar reaches 100% because the bar-rendering goroutine is not synchronized with upload completion.</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 working Go CLI with Cobra for my Google Drive sync project, moving the folder-upload logic behind a sync command. I create a cmd package with a root command, wire Cobra’s Execute call into main.go, then add a sync subcommand that loops over the configured folders and uploads them to Drive. I land with a usable command—go run . sync—and outline that I’ll add selective-sync arguments and more commands in later parts.</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>I reorganize my Google Drive syncer&apos;s Go codebase into proper packages before building the CLI, because maintenance work matters as much as shipping features. I move shared types into an internal/models package, split OAuth into an auth package, rename my sync package to api since it holds all the Google Drive API code, and export the fields I need across package boundaries. Along the way I hit two real Go gotchas — you can&apos;t define methods on types from another package, so Connect becomes a plain connectToDrive function, and a nil pointer dereference appears because I never called that function to assign the Drive service. After fixing both, the full upload runs and syncs my newly organized files to Drive, so the code is finally clean enough to build the CLI next.</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 wire up automatic OAuth token refresh for the Google Drive syncer so I never have to manually re-authorize access again. I build a testAccess function that hits the Drive files list endpoint with a page size of one to check whether the current token still works, and map the result to three states: access granted, access expired, or access rejected. Google returns two different error messages for an expired token (token expired and token has been expired), so I catch both before falling back to a hard rejection. The change that actually removes the manual step is config.TokenSource in the get client function: it refreshes the access token when it expires, reuses it when it&apos;s still valid, and I save the refreshed token back to disk and build the client with oauth2.NewClient. The result is that the program reconnects on its own, and I can finally focus on features instead of re-authenticating.</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 uploads to my Google Drive syncer so each project&apos;s ignored files and my global ignore list are skipped automatically. I build a readGitIgnore function that scans each project&apos;s root .gitignore line by line, trims whitespace, and returns the entries, then combine that with the global ignore list from config.json when useGitIgnore is true. In filepath.WalkDir I compute the full ignored path and skip any path that equals it or sits under it, returning filepath.SkipDir for directories and nil for files. My take is this is a small feature but essential for production use: anything you wouldn&apos;t push to source control, like client.json or token.json, shouldn&apos;t go to Drive either, and the test run confirms those sensitive files never get uploaded.</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 show how I make DriveSyncer handle duplicate project uploads and nested subfolders in Google Drive by deleting and re-uploading the previous project folder and creating subfolders with the correct parent IDs. For duplicates, I choose the simple delete-then-reupload route instead of tracking checksums for every file, because per-file change tracking gets complicated fast. I query Drive for a folder by mime type, name, parent, and trashed=false, limit results to one, and handle zero, one, or multiple matches; if deletion fails, I log it and continue because duplicates are not critical. For subfolders, I create each folder with its parent ID, keep a map from subfolder name to Drive ID, and use filepath.Base and filepath.Dir to resolve each file’s parent before uploading it to the right place.</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 half of my Drive Syncer by creating Google Drive folders and pushing every local file inside a configured project folder up to Drive. I create the base folder first, query Drive by mimeType, name and trashed=false so I don’t create duplicate folders, then create each project folder inside it using the application/vnd.google-apps.folder MIME type. From there I walk the local folder with filepath.Walk, skip directories, open each file, and upload it with files.create plus Media, setting the project folder ID as the parent. I use log.Fatal when base or project folder creation fails because there is no point continuing without a destination. I loop over the folders in config.json, and I verify in Google Drive that the project folder and all files uploaded correctly.</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 refactor the Google Drive syncer so the OAuth flow and Drive service setup live in a Drive struct with a Connect method instead of bloating main, then add a JSON config file that drives what gets synced. The config holds a base folder, a list of project folders with absolute paths, a global ignore list, and a useGitIgnore flag so each project&apos;s .gitignore is respected automatically. I read it with a single json.NewDecoder call, and I derive folder names from paths with filepath.Base, only filling them in when the config leaves the name empty so a custom name can override the default.</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 start building a custom Google Drive syncer in Go because GitHub alone did not save me when my hard drive corrupted and I lost local changes that were never pushed. Google Drive’s desktop app can back up whole folders but cannot ignore .git, node_modules, virtual environments, .env files, or credentials the way .gitignore does, so I’m building my own sync tool with proper exclusions. I walk through creating a Google Cloud project, enabling the Drive API, configuring the OAuth consent screen, creating a desktop OAuth client, and keeping client.json secret. Then I implement the Go OAuth flow: load or request a token from the web, save token.json with owner-only permissions, use the drive scope, add myself as a test user, authorize the app, and list the first 10 Drive files to verify access. OAuth and API calls work; uploads and filtering 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>Scaling responsive typography proportionally is a mistake because large headings need to shrink much faster than body text. I demonstrate this on a sample article: the main heading drops from 60px to 30px (50%), the subheading from 30px to 20px (33%), and body text only from 18px to 16px. My take is that there is no good ratio or formula tying these elements together; resize each independently, with the only reliable rule being that larger text should shrink 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 break down how Go’s garbage collector works: it’s non-generational, concurrent, and uses a tricolor mark-and-sweep algorithm. I explain why Go skips generational collection — compiler optimizations keep most values on the stack, so the generational hypothesis buys little — and walk through mark-and-sweep: a brief stop-the-world at the start of marking to enable a write barrier, then concurrent tricolor marking where white means unused, blue means processing, and brown means in use, followed by sweeping away the still-white objects. I also cover the GOGC environment variable, which sets the heap growth target before the next GC cycle: default 100 means GC runs when heap memory doubles, 50 means it runs after 50% growth, and setting it to off disables GC entirely. My take is that the default is fine for most programs, but GOGC is there if you need to tune collection frequency.</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>Go runs a garbage collector, but underneath that every variable ends up either on the stack or the heap, and the compiler chooses using escape analysis. I start with why memory management matters at all — C and C++ force manual allocate and free, Rust&apos;s ownership model makes the compiler handle it, and Go automates it with a garbage collector — then go deep into the two structures Go actually uses. The stack holds local variables in LIFO order, is automatically managed, and needs no garbage collector; the heap is slower, stores objects behind pointers kept on the stack, and is where garbage collection runs. I walk through the escape analysis rules the compiler applies to every variable: returning a pointer, using maps, slices or structs, closures capturing outer values, and storing values inside complex objects all push data to the heap. Stack allocation is much faster, so these rules matter if you&apos;re building performance-critical Go.</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 by wiring up the persistence and general categories, then fix an AOF bug where restoring the append-only file ignored the server&apos;s memory limit. Most of the work is plumbing: tracking RDB last-save time and save count, AOF rewrites and rewrite-in-progress, total connections received, total commands processed, and expired and evicted keys, with each counter incremented where the event actually happens. Testing exposed a bug — my TTL handler was expiring keys on its own instead of going through TryExpire, so INFO never counted them; routing it through TryExpire fixed it. For AOF, the sync function was passing an empty config, so I now pass only maxmemory, eviction policy and mem samples, since passing the whole config would re-append every restored value to the AOF. With the INFO output matching what Redis actually returns and the AOF respecting the 256-byte limit in testing, this closes out the series.</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>The INFO command is the last major piece of my Redis clone, and in this part I build out the server, client, and memory sections of its response. I track server start time and peak memory in the app state so uptime comes from a single time.Since call and peak usage is a true high-water mark updated inside db.Set, pull the PID, executable path, and microsecond server time straight from the os and time packages, and count connected clients by incrementing in handleConn and decrementing with a defer. For total system RAM I pull in gopsutil v4.25.8, since Go has no built-in API for it, and I return db memory, peak, max memory, and the eviction policy from config. The persistence and general categories plus the command handler land in the final part of the series.</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 bug in my Go Redis clone, refactor the code into focused files, and then implement the MONITOR command so one client can receive every command other clients run. In DB.Get, I show why every return path must release the read lock and why defer is the safer default when the lock is held to the end of the function, though I still unlock early when long non-shared work follows. I split items, transactions, app state, and clients into their own files, remove the unnecessary wait group because Accept blocks the main thread, and explain how I’d implement the volatile eviction policies with a separate expirable-key store instead of scanning the whole database. For MONITOR, I add a monitors list, remove clients on disconnect, and relay each command asynchronously with a Unix timestamp, client address, and quoted 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 Redis&apos;s allkeys-lru and allkeys-lfu eviction policies in my Go Redis clone by sorting sampled keys with the metadata already stored on each item, then reusing my evict-until-memory-freed loop. For allkeys-lru I sort by lastAccess; for allkeys-lfu I sort by access count. I demo both against the server with a 256-byte limit and confirm keys are evicted, and I flag that AOF replay currently bypasses maxmemory because it runs with an empty config. I also explain how the remaining volatile policies will work: filter to keys with expiry, then apply random/LRU/LFU, with volatile-ttl evicting the soonest-to-expire keys 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 AOF file sync in my Go Redis clone by making every array read share one buffered reader instead of creating a fresh reader each time, which was causing replay to restore only the first key. I also change ReadBulk to return values and errors, and I break out of AOF parsing when a bulk string scan fails because the command is already invalid. After the fix, replay loads all three keys from the AOF backup instead of just the name key.</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 by sampling a bounded set of keys and deleting until memory is freed. I show why Redis-style eviction samples only a handful of keys instead of scanning the whole store — my server uses a configurable max-memory-samples of 50, while Redis defaults to about 5 — because full scans kill throughput. I add an Item struct with lastAccess and accesses fields to lay groundwork for LRU/LFU, a get helper that updates those fields, and reusable enoughMemoryFreed/evictUntilMemoryFreed helpers; then I test that setting num2 past 256 bytes evicts the name key while num1 and num2 remain.</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 real memory management to my Go Redis clone: every key now reports its approximate footprint, and the server refuses writes once it hits a configured max-memory limit. I calculate key size from the string header (16 bytes), the expiry field (24 bytes), the Go map entry metadata (32 bytes), plus the key name and value lengths, and I argue that approximating is the right call because exact byte-by-byte accounting is cumbersome and error-prone. I track a running db.memory total in set and delete, subtract the old usage when a key is overwritten so re-setting the same key doesn&apos;t inflate the count, and guard delete against missing keys to avoid nil pointer panics. On the config side I add max-memory and max-memory-policy directives with a parseMem function that converts 256KB, 256MB, 256GB or raw bytes into an int64. Only no-eviction is implemented for now, so exceeding the limit returns a maximum memory reached error to the client.</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, adding MULTI, EXEC, and DISCARD command handlers and a transaction queue that holds commands until execution. I first demonstrate how official Redis queues every command after MULTI and returns an array of replies from EXEC, then build the same behavior: store the active transaction in app state, reject nested MULTI calls, queue commands in the handle function, and preallocate the replies slice to the queue length and assign by index instead of appending for performance. My take is that Redis transactions are much simpler than MySQL or Postgres transactions, and the implementation comes down to careful state management and command queuing.</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 so the append-only file stops replaying outdated SET commands and keeps only the latest value for each key. I run the rewrite in a background goroutine, take a read lock, copy the database, release the lock, then truncate and seek the AOF file to zero before writing one SET command per key from the snapshot. While the file is being rewritten, I reroute new AOF records into an in-memory buffer and swap the writer back to the file when the rewrite finishes. I also fix my RESP parser: the old read-array/read-bulk code only handled lengths under 10 because it read a single byte, so I add a readLine helper, trim CRLF, parse the full length line after the type byte, and use io.ReadFull for bulk payloads. The result is a compact, correctly replayable AOF and support for commands and values longer than nine characters.</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>EXPIRE and TTL need each key to carry its own expiry timestamp, so I change the store from a map of strings to a map of *Key structs holding the value and an expiry time.Time, then fix every handler that breaks. I add Set and Delete methods on the Database pointer receiver so handlers use a public API instead of constructing internal structs, and I lock the mutex only around the shared store. EXPIRE takes a key and seconds and returns 1 or 0; TTL returns -2 for missing keys, -1 for persistent keys (detected by comparing the expiry&apos;s Unix time to the epoch), and remaining seconds otherwise. I add lazy deletion in GET and TTL when the expiry has passed and fix a nil pointer dereference from checking comma-ok too late. I explain Redis&apos;s real two-part expiry — lazy checks plus a background sampler — but leave the background process for later.</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 for my Go Redis clone by adding a requirepass directive, tracking authentication state per client, and blocking unauthenticated commands. I add requirepass to the config parser and store the password plus a require flag on the config object. Each connection becomes a client struct with net.Conn and an authenticated boolean; AUTH accepts exactly one argument, compares it to the configured password, and flips that flag. Unauthenticated clients can run only safe commands like COMMAND and AUTH, while everything else returns a no-authentication error; I also make invalid commands return an error so clients do not hang. I land on this as a solid basic password layer, with Redis-style ACLs as a more complex possible follow-up.</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 the RDB save path in my Go Redis clone so a snapshot is only considered successfully written when the in-memory buffer and on-disk file hashes match. I encode the database to a bytes.Buffer, hash that buffer, write the bytes to the file, call Sync to flush OS buffers to stable storage, seek the file back to the start, hash the file, and compare both checksums. The save now succeeds with matching hashes; I postpone the AUTH command to the next part because the episode was already running long.</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, and I show why the obvious way to flush a database is the wrong one. For DBSIZE I take a read lock and return the length of the store map, while FLUSHDB takes a write lock. My main point is that looping over every key and calling delete gets the job done but scales terribly once you have tens of thousands or millions of keys, so instead I just assign the store a brand-new empty map, which wipes the old one in one step. I test both commands against a store loaded from the AOF file: DBSIZE returns 4, FLUSHDB returns OK, and DBSIZE then returns 0.</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 the RDB layer: a read lock in saveRDB so snapshots don&apos;t race with writes, an os.O_TRUNC flag so every save overwrites stale data, os.Open instead of os.OpenFile in syncRDB, and an explicit file close on the error path. SAVE works but holds a read lock for the entire encode, blocking all writes on a large store, so I build BGSAVE instead: it copies the database under a brief read lock, flips a bgSaveRunning flag, and encodes the copy in a goroutine while the server keeps serving clients. My take is that Go&apos;s garbage collector rules out copy-on-write, so this isn&apos;t true background saving — the copy itself is still expensive — but freeing the mutex before the encode is the real win.</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 the concurrency bug that crashed my Go Redis server the moment a client disconnected, then implement the DEL, EXISTS, and KEYS commands. Connections are now accepted in an infinite loop, handed to a goroutine tracked by a WaitGroup, and closed gracefully — two clients can read and write the same shared store at once. DEL returns the number of keys actually removed rather than the number passed, EXISTS counts matches under a read lock, and KEYS does glob-style matching via filepath.Match, which also means adding an Integer reply type. My take: KEYS is fine for a toy project but too expensive for production, where you&apos;d reach for SCAN — next up is SAVE and BGSAVE.</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 implement RDB data persistence for my Go Redis clone by snapshotting the in-memory store to disk and reloading it on server start. I add per-snapshot trackers that count changed keys, use time.Ticker goroutines to check whether the configured key threshold has been met within the configured seconds, and save with Go’s gob encoder when it has. I show the config-driven save rules (for example, save 5 3 means three key changes in five seconds triggers a snapshot), wire incrementRdbTrackers into SET, and fix a duration bug by multiplying by time.Second. On startup I open backup.rdb read-only and decode gob directly into the database store in one line, which is simpler than replaying AOF commands. I land with both AOF and RDB persistence running together; next I’ll add DEL, EXISTS, and KEYS.</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 persistence for my Redis clone in Go, showing how Redis can durably replay SET commands from disk instead of relying only on in-memory state. I explain the two Redis persistence approaches — RDB snapshots, which serialize the whole database to bytes, and AOF, which logs each write as a RESP-encoded command and replays those commands on startup — and then build the AOF side. I add an AOF struct around a buffered writer, open backup.aof in append mode, extend my RESP writer to serialize arrays recursively, and wire an app state through the command handlers so SET can log itself. I implement all three fsync modes: always flushes after every write, every second uses a ticker goroutine, and no leaves flushing to the OS. On startup I replay every AOF record through the SET handler with a blank state to rebuild the database. It works, and I’ll tackle RDB 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 to my Go Redis clone by wrapping the store in a sync.RWMutex — ReadLock for GET, Lock for SET — so multiple clients can hit the same commands without race conditions, then build a parser for a minimal redis.conf. The parser splits each line on spaces and switches on the leading keyword to fill a Config struct with the data directory, both RDB save rules (900 seconds/1 key, 300 seconds/10 keys), the RDB filename, and the AOF settings including the always/everysec/no fsync modes. Reading the config also creates the data folder with os.MkdirAll at 0755, and I land by splitting the bloated main.go into writer.go, handlers.go, db.go and value.go and confirming SET and GET still work through redis-cli. Accepting multiple connections is deliberately deferred; the config exists so I can wire up persistence next.</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 build the actual data layer of my Go Redis clone: GET and SET handlers over an in-memory map, plus a Writer abstraction that serializes my Value structs into RESP so I never hand-construct plus signs and CRLF pairs at every call site. First I walk through RESP properly — + for simple strings, - for errors, $ for bulk strings, and * for arrays, which is how every Redis client sends its commands; bulk strings carry a byte length because unlike simple strings they can contain arbitrary content and the parser needs to know exactly how many bytes to read for performance. Handlers are a func(*Value) *Value type kept in a map keyed by command name, and one handle function pulls the command from the first array element, dispatches, and writes the reply. Two gotchas bite me: I have to flush the bufio writer or replies sit in the buffer and the client hangs, and I have to answer COMMAND with an OK or redis-cli never starts. Concurrency and persistence are 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 the first working layer of a Redis clone in Go: a TCP server on port 6379 that speaks enough RESP to parse real redis-cli commands. I install Redis only to use its client, stop the official server, then implement a net.Listen accept loop and a Value struct with readArray and readBulk methods that decode arrays, bulk strings, and simple strings, including length prefixes and CRLF trimming. I land with redis-cli connecting to my server and commands arriving as structured data; storing and retrieving keys with GET, SET, EXIST, and DEL is left for the next part.</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 an icon library for a design project by comparing real icon sets and the tradeoffs that matter. I argue that you should stick to one library for consistency, choose a large comprehensive pack like Material Symbols with 3,000+ icons rather than a small set like Hero Icons or Radix at around 300, and match the icon style to your brand—rounded and playful like Hero Icons versus sharper Material or Bootstrap icons. I also show why SVG icons beat font icons for customization and animation, and why packs with multiple states such as outlined and filled give you useful flexibility for active and inactive UI. My take is that consistency and coverage matter more than mixing packs for minor stylistic preferences.</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 show why you should never stretch UI elements to fill the screen and how to size each component to its ideal width instead. I demonstrate this with a login form: removing its 576px max-width makes it stretch across the page and look terrible, while constraining it to its ideal width keeps it clean even with an empty background. I then argue that CSS grid’s relative columns are dangerous for layouts like sidebars: a 25% sidebar looks fine on a normal screen but becomes huge on a large display, so I fix it with an exact 256px width and let the main content take the remaining space. My take is that empty space is fine, over-designing to fill the page adds noise, and you should use width or max-width instead of grids for everything.</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 where reduced opacity and size make the current testimonial obvious at a glance. I demonstrate that when an element is already heavily emphasized—large image, bold text—you often can&apos;t push it further, so lowering the emphasis of related inactive elements creates relative emphasis without changing the active element much. My take is that this works best for lists or series where only one or two items can be active at a time, and it&apos;s another core visual hierarchy trick for designing clearer pages.</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>Most designers ruin dark mode by naively inverting their light-mode colors, so I break down the four tactics I use to build dark interfaces that are actually pleasant to read. I show why pure black backgrounds create harsh contrast and swap them for a dark gray like gray-900, explain why outlined icons rendered in gray-300 beat filled pure-white icons, and demonstrate the subtle image treatment — brightness 0 with contrast bumped to 1.2 — that makes bright photos sit comfortably on a dark canvas. I also wire up the prefers-color-scheme media query so the correct theme applies automatically from the user&apos;s OS setting. None of these changes is big on its own, but they compound into a UI that feels elite.</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>Stop styling buttons by semantic color meaning and start styling them by visual hierarchy. I put two identical UIs side by side — one with green, blue, and red semantic buttons where every action screams equally, and one where preview, edit, and delete clearly rank — to show why the semantic version leaves users with no idea what to do next. I argue the delete action should almost always be the least prominent thing on the page, and I live-code the fix for when your brand color (say, red) fights the semantics: swap in black, white, or gray so the UI stays clear. Semantic colors are fine when a page has a single action; the moment multiple actions compete, hierarchy wins.</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 instead of reaching for LazyVim or Kickstart, because prebuilt distros get you productive but don&apos;t teach you how Neovim works. I create ~/.config/nvim/init.lua, split configuration into lua/conf modules, translate classic Vim settings in set.lua, add lazy.nvim as the plugin manager, and set up nvim-treesitter with auto-install so I get syntax highlighting and parsers for the languages I use. By the end, line numbers, relative numbers, and syntax highlighting work, but my Tab leader mapping is still broken; that&apos;s enough for part one, and I&apos;ll keep building toward making Neovim my default 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 explain how to actually create visual hierarchy by deciding whether the data or the labels deserve emphasis, then using font size, weight, color, spacing, borders, and shadows to make that hierarchy obvious. I start from the rule to avoid labels where context already does the job: a profile card with name, role, company, and hobbies does not need rigid name-value labels, so I show how removing them and emphasizing the name and profile picture fixes an otherwise ugly component. I also cover the caveats: always use labels in HTML forms for accessibility and UX, keep labels or icons as clarifying text when numbers cannot stand alone but de-emphasize them so the data stays focal, and flip the hierarchy on information-dense pages like phone specs where users scan for labels. My take is simple: no matter which side you emphasize, always build a clear sense of visual hierarchy.</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>Choosing between headline and body fonts comes down to two measurable properties — x-height and letter spacing — and I compare real fonts side by side to show how each one signals the job a font was designed for. I set Jost (a headline font) next to Montserrat (a body font) at the same 48px and 400 weight: Montserrat has a noticeably taller x-height and wider letter spacing, which is exactly what makes small text readable, while Jost runs shorter and tighter because large text doesn&apos;t need the help. I also cover display fonts, which look striking on big headlines but become unreadable at 16px, so they should never touch body copy. My take is that you don&apos;t need to overcomplicate font selection — follow x-height and letter spacing 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 an Electron + React + TypeScript + Tailwind starter from scratch with Webpack, then wire up development hot reload, production builds, and electron-builder packaging into an installable desktop app. I show why Electron&apos;s main and renderer processes need separate Webpack configs, how the dev script uses concurrently, wait-on, and cross-env to launch the React server before Electron, and how production loads dist/renderer/index.html instead of localhost:3000. I also cover the packaging gotchas: Visual Studio build tools on Windows, running electron-builder as administrator the first time, and pinning Electron to 37.2.4 when a minor update broke packaging. My take is that building this setup yourself is worth it because you learn what actually happens under the hood.</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 systemize typography by breaking it into four decisions: font families, font sizes, letter spacing and line height. For font families, I narrow the choice by category — serif, sans-serif, display or monospace — then pick from Google Fonts, favoring fonts with 10 or more styles because better-crafted type tends to come with more weights, and because loading real bold and italic styles avoids the browser&apos;s ugly faux-bold compensation. I also show how to study competitors&apos; fonts through dev tools, and argue that internal apps should skip custom fonts entirely and use system fonts to speed things up. For sizes, I use a handpicked exponential scale with more options at the small end and fewer at the top, defined in rems and never ems. Rather than handing you line-height and letter-spacing values to copy, I explain the rules: tighter tracking on large headlines, and line height that scales inversely with text size.</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>I argue that rems are the best unit for typography because they scale with the user&apos;s browser default font size and zoom, while pixels ignore those accessibility settings. I explain how one rem maps to the root HTML font size (16px by default) and show browser settings and zoom changing that value, then land on using rems for font sizes and for margins around text so spacing scales with readability; clamp() is the next step for 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>I explain why HSL is the best CSS color format and how hue, saturation, and lightness map directly to how we perceive color. I show how hue is a 0–360 position on the color wheel, saturation controls gray-to-full-color, and lightness controls black-to-white, then argue that this makes colors far easier to imagine and reason about than hex or RGB. I land on using HSL wherever possible—in CSS and design tools like Figma or Sketch—because it makes color decisions clearer and more intuitive.</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>Color should never be the only thing distinguishing elements in a UI, because red-green colorblindness — the most common form — wipes that information out entirely. I demonstrate it on a finance dashboard where profit and loss are shown only with green and red numbers, then desaturate it: the two states become impossible to tell apart, and adding up and down icons fixes it without changing a single color. From there I argue that links need an underline or some other non-color differentiator (navbars are the exception, since convention already tells users they&apos;re links), and that the single best practical method is to build the entire interface in grayscale first and add color last, so color supports the hierarchy instead of carrying it.</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>Picking good fonts comes down to a repeatable process, not years of trained instinct: I sort every typeface into four categories — serif, sans serif, display (script included), and monospace — then match the category to the brand&apos;s personality, checking competitor sites with browser dev tools whenever I&apos;m unsure. From there I filter Google Fonts by category and pick fonts with 10 or more styles, because style count is a reliable proxy for how much care the designers put in, and because a missing style forces the browser to fake it — I show Poppins bold 700 rendered natively next to the browser&apos;s thickened imitation to prove how bad that looks. I also verify language support and match fonts to the job using x-height and letter spacing: tight spacing and a short x-height for headlines, wide spacing and a tall x-height for body text. When I&apos;m stuck, I sort by popularity and trust the crowd.</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 a handful of simple typography rules can make any article on the web dramatically more readable, and I apply them live to a deliberately broken page to prove it. I cover limiting line length to roughly 50–75 characters (I usually go 80–85, but that&apos;s pushing the boundaries), scaling line height inversely with font size and directly with line length, using asymmetric margins so a heading clearly belongs to the paragraph below it, and defaulting to left alignment for nearly all text. My take is that left align is the best default for about 90% of text, center align only works for a sentence or two, and right align is mostly pointless outside numeric tables and right-to-left languages. By the end I take an ugly article and turn it into a pleasant reading experience with just those few changes.</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>I build a design system&apos;s design tokens — colors, spacing, typography, borders, shadows — before I design a single screen, because standardizing those atomic values into reusable variables is what makes design fast and consistent. I explain why tokens come first: they encode your brand identity, kill pointless decisions like 18px versus 20px margins, and give every screen a natural symmetry and consistency. Then I walk through my beginner process: pull brand and semantic colors plus 8–12 shades each from coolers.co, pick open-source fonts from Google Fonts, use a linear scale for color shades but exponential scales for spacing and typography, and store everything as CSS or Figma variables. My take is that a design system is a spectrum, not a binary — the more tokens you define up front, the less you labor over low-level design decisions.</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>Most people get mediocre results from AI because they type two sentences and expect AGI-level output; the fix is writing detailed prompts — mine these days run a couple thousand words — that leave nothing to the model&apos;s imagination. I walk through a simple five-step framework: pick the task you want to automate, break it into beginner-level step-by-step instructions, add example inputs and outputs, specify the exact output format, then test and iterate. I demonstrate it with a real prompt I built to generate viral video titles from a PDF script — assigning a role, giving 15 specific principles, requiring 10 titles ordered best to worst, and explicitly banning extra text like &apos;Absolutely, I&apos;ll do that for you.&apos; My take: current models on GPT, Claude and Grok are good enough to automate almost any task, but only if the prompt does the heavy lifting.</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 dynamic CSS Grid layout with zero media queries by combining repeat(), auto-fit, minmax(), and nested min()/max() column sizing. I show how the grid class expands to repeat(auto-fit, minmax(min(100%, max(300px, 100% / 4)), 1fr)), explain why dividing by 4 leaves room for gaps and at least three columns, and use one md breakpoint only to make the first and last cards span two columns. My take is that the nested min/max math is what keeps the layout from overflowing on small screens while letting columns fill available space on large screens; I encourage you to recreate it and experiment with the values.</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>Most empty-state UIs get ignored until a new user lands on a blank screen, so I show how to design and build empty states that explain the problem, describe the next step, and give a clear CTA. I walk through job board applications, notifications, messages, search results, and an empty cloud drive, showing why illustrations are mostly aesthetic while clear actions are essential. I argue that generic messages like “no application sent” fail because they don’t tell users how or where to act, and that every empty state should guide users to the next one or two steps.</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 makes typography responsive with a single line of code, replacing the stack of media queries I used to resize text at different breakpoints. I break down clamp(min, preferred, max): the minimum and maximum keep text from shrinking or growing too far, while the preferred value—usually a mix of vw and rem—controls how fast it scales in between. I demonstrate why raw vw alone fails because it has no bounds and scales too aggressively, then use a clamp calculator to generate values from min/max font sizes and viewport ranges. I also argue that the preferred value must include a rem component so browser zoom still works and the text stays accessible. My take is that clamp() is now the standard way to handle responsive text, and one line can cover headings, body copy, and small text 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 responsive build of my Next.js and Tailwind travel-agency landing page by fixing the social, CTA, and footer sections. I replace the CTA’s two images with a single absolutely positioned background image using mix-blend-multiply, lowered opacity, and z-index so the text stays readable and selectable, then re-add a container and padding to keep content off the edges. For the footer, I swap the grid for flexbox with basis-zero/grow columns, constrain the input’s width with min/max values and flex-wrap, add wrapping to the bottom row, underline the clickable link, and fix the icon attribute names. I land on a fully responsive site across all screen sizes, with design, responsiveness, and functionality complete.</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 responsive, working section by section from the offers area down through reviews. I replace rigid grids with flex-wrap layouts using basis-0, grow and min-width constraints, restrict absolute testimonial overlays to the sm breakpoint, hide decorative images on small screens, and use clamp() for scrolling-offer text so it scales cleanly. For the reviews slider I add a pause/play control with client-side state passed into the slider component, because mobile users can&apos;t hover to pause the animation, and I keep that control visible on desktop for accessibility rather than hiding it.</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 desktop-only Next.js and Tailwind travel agency landing page responsive in part one of a three-part mobile pass, starting with a 480px extra-small breakpoint and horizontal container padding. I fix the super header and navbar with flex-wrap, order, and breakpoint resets, then add a client-side mobile menu with a screen-reader-labeled icon. I replace oversized hero and section-heading text with clamp() sizing across a 320–1440px viewport range, make the benefits section stack to one column and return to three at MD while constraining the image width, and convert destinations to a flex-wrap layout with container queries so the inner content shifts from column to row. By the end, the navbar, headline, section headings, benefits, destinations, and arrow controls all reflow cleanly from mobile to desktop.</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 build the footer for a travel agency landing page in Next.js and Tailwind, completing the site&apos;s design before moving on to responsive work. I create a dedicated Footer component with a slate-900 background, reuse the navbar brand styling, add a short email subscribe form, Bootstrap social icons with white-to-orange hover states, and two quick-links columns arranged in a four-column grid where the brand section spans two columns. I finish with a subtle divider, copyright text linking to my site, and a back-to-top control using a double-arrow icon and brand color. My take is that the page design is now done; the next step is making the whole thing responsive on smaller screens.</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>The CTA section is the second-to-last piece of my travel agency landing page, and here I build it: centered content flanked by two stock images on a yellow gradient background. I extract the navbar button into a reusable PrimaryButton component that takes children instead of a text prop, so it can hold an external-link icon, and reuse it in both the navbar and the CTA. I walk through the layout details that make it work — a flex column with items-center and text-center, a paragraph capped at max-w-2xl, and images constrained to max-w-100 and h-80 with object-cover and object-center so they crop consistently. I land on a gradient from yellow-200 to yellow-300 at 60% for the background, which looks far better than the flat color, and drop the top padding while keeping bottom padding. That leaves only the footer.</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 photo grid for the travel agency landing page in Next.js and Tailwind, using a hover overlay to reveal an Explore More button. I create a posts array with image and location fields, render it in a flex row with gap, and wrap each image in a relative, overflow-hidden container. On hover, a white overlay fades to 80% opacity over 300ms and the image scales, with an Instagram icon and external Explore More button inside. That completes this section of the landing page.</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 for a Next.js and Tailwind travel-agency landing page, then wire it into the existing layout. I generate context-appropriate testimonials with GPT in JSON so I don&apos;t have to hand-format the data, extract the marquee animation from the scrolling office section into a reusable Slider component, and reuse it for the reviews. I fix the card layout with wrapping, min-width, quotes, separators, and even/odd offsets, then switch from five to six testimonials so the offsets line up. I finish with randomized card colors and gradient overlays that make the reviews fade into the background. My take is that extracting the slider instead of rebuilding it keeps the animation consistent and cuts the work.</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 Travel Essentials product grid for the Travel Agency landing page with Next.js and Tailwind, creating a new products.tsx section and rendering four product cards from a small title-and-image data list. I reuse the existing heading-label component, map over bags, tools, camping gear, and suitcases, then fix the layout with flex basis-0/grow, object-contain/center images, and an overflow-hidden image container. I finish the section with a group-hover scale-115 image effect using Tailwind v4 arbitrary values, duration-300, cursor-pointer, orange-50 background, extra padding, 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 horizontally scrolling, animated offers section in Next.js and Tailwind, starting from a reusable component and an offers array with text, link, and icon fields. I pull five icons from Compose Icons, render each offer as a link, and fix casing errors by converting icon exports to camelCase. The core trick is duplicating the offer list inside an overflow-hidden, no-wrap container and animating it with a CSS keyframe from translateX(0) to translateX(-100%) over 9 seconds, linear and infinite. I also pause the animation on hover using animation-play-state: paused, and land on a clean looping marquee that cuts off at the edges and reads well.</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 finish the travel-agency landing page in Next.js and Tailwind by building the special-offers section and a second “more offers” grid. I extract a reusable SectionHeading component and a WhiteExternalButton with an optional bordered prop defaulting to false, so the same button stays borderless in the hero but gets a visible border in offers. I lay out the first offer as a two-column grid with an image and an absolutely positioned testimonial card rounded to match, then build the second offers section as a three-column grid with the first card spanning two columns, equal-width flex children via basis-0 and grow, fixed-height cards with overflow hidden, and orange/sky backgrounds. I also settle for a percentage icon as the discount icon after searching 14,000 icons. The result is a clean, reusable set of offer sections that fit the site’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 in Next.js and Tailwind, from the heading and data list through the destination cards, price badges and carousel controls. I fix the reusable heading label with inline-block so it stops taking full width, generate placeholder destination JSON with ChatGPT, swap in real Unsplash images, and render a three-column grid of rounded cards with aspect-square object-cover images, unique background colors, and a translucent price overlay. I finish the section with a separator line, flex-aligned back and forward arrow buttons with accessible SVG labels, and a background image plus white gradient overlay using isolate and a negative z-index. My take is that these small layout, accessibility and styling fixes are what make the section come together cleanly.</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 for a travel agency landing page with Next.js and Tailwind, continuing the Travel Buddy build from the hero section. I create a reusable HeadingLabel component with a plane icon, add the bold headline with “Buddy” in orange-600, place a stock image between two groups of benefits, and render four benefit cards with icons and typed props. I arrange the cards in a three-column grid with separate left and right groups so the image stays centered, then tighten spacing and gaps until the section matches the intended layout. My take is to build the reusable pieces and typed data structures first, then style and arrange them.</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 super header, navbar, and hero section of a travel agency landing page with Next.js, TypeScript, and Tailwind CSS, starting from a blank app and ending with a styled hero. I create a reusable Container component, keep icons in a shared icons.tsx file with sr-only labels for accessibility, and wire up currency, coupon, login/signup, nav links, search/favorite icons, and CTA buttons with hover states. For the hero, I add a background image, a gradient overlay using Spotify-style transparent black/white stops, and fix text selection and stacking with z-index plus isolate; I also switch to the Poppins font and adjust Tailwind v4 opacity utilities.</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>What&apos;s new in Tailwind V4</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>Tailwind Masonry Grid Layout Tutorial - Fully Responsive</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>i&apos;m all over the place with this video lol</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>first time uploading to YouTube so i&apos;m sure there&apos;s a lot of room to improve. audio is kinda terrible, im sorry. i&apos;m working on fixing that :p</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>
