<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss version="2.0">
	<channel>
		<title>Founder's blog</title>
		<link>
		https://www.jitbit.com/
		</link>
		<description>Blog by Alex Yumashev, founder and CEO of Jitbit Software, the company behind Jitbit Helpdesk</description>
		<language>en-us</language>
		<docs>http://blogs.law.harvard.edu/tech/rss</docs>
		<generator>Jitbit RSS-Generator 1.1</generator>
		<pubdate>Thu, 12 Feb 2026 21:24:27 GMT</pubdate>
		<lastbuilddate>Thu, 12 Feb 2026 21:24:27 GMT</lastbuilddate>

			<xhtml:meta content="noindex" name="robots" xmlns:xhtml="http://www.w3.org/1999/xhtml"/><item>
				<link>https://www.jitbit.com/alexblog/323-i-asked-claude-code-to-remove-jquery-it-failed-miserably/</link>
				<guid>https://www.jitbit.com/alexblog/323-i-asked-claude-code-to-remove-jquery-it-failed-miserably/</guid>
				<title>I asked Claude Code to remove Jquery. It failed miserably.</title>
				<description>&lt;blockquote&gt;&lt;i&gt;Disclaimer: this is a rushed angry rant with F-bombs all over. I had a rough day alright. If explicit language is an issue, please skip the read.&lt;br&gt;&lt;br&gt; Also for context - I'm a heavy AI user and my productivity has genuinely gone up 10x thanks to these tools. But fuuuuuuu....&lt;/i&gt;&lt;/blockquote&gt;&lt;!--more--&gt;

&lt;p&gt;Big parts of our app still use jQuery (oh shut up) and we're migrating away from it. Nothing ambitious, just replacing DOM manipulation and event handling with vanilla JS. Our complex interactive screens already use Vue.js, but dozens of simple admin pages still have little jQuery sprinkled around.&lt;/p&gt;

&lt;p&gt;So I thought: hey, perfect job for an AI agent. Repetitive, mechanical, well-defined, long and boring. Enough with my small local rewrites, let's try the autonomous agent, I wanna be like those youtubers.&lt;/p&gt;

&lt;p&gt;So I pointed my MAX priced Claude Code (Opus 4.6) at the codebase. Poured some coffee.&lt;/p&gt;

&lt;h2&gt;It failed fucking miserably.&lt;/h2&gt;

&lt;p&gt;OK I didn't just yolo it, I did my homework. Wrote a comprehensive &lt;code&gt;CLAUDE.md&lt;/code&gt; with detailed instructions. Prepared helper functions - like &lt;code&gt;const _id = id =&amp;gt; document.getElementById(id);&lt;/code&gt;. Described edge cases, like, Jquery being forgiving for non-exsiting elements, while plain JS is not, so we have to use optional-chaining.&lt;/p&gt;
&lt;p&gt;I even wrote my custom &lt;code&gt;fadeIn&lt;/code&gt; and &lt;code&gt;fadeOut&lt;/code&gt; replacements on &lt;code&gt;HTMLElement.prototype&lt;/code&gt; using CSS transitions, so &lt;code&gt;$(#something).fadeIn()&lt;/code&gt; just becomes &lt;code&gt;_id(something).fadeIn()&lt;/code&gt;. Then I went through the "plan mode" and set up two separate agents - one for writing code, one for reviewing it. Becasue one simple agent is so 2025.&lt;/p&gt;

&lt;p&gt;Overall the task was to edit 67 files, about 5-10 lines to rewrite in each. Simple AI-enhanced find and replace.&lt;/p&gt;

&lt;p&gt;Not exactly rewriting a fucking C compiler in Rust from scratch or whatever they claimed it did.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;It was a total shitshow.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Why AI is so bad at vanilla JS and HTML, when there's no React/Vue in a project? Just a couple of examples:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;1.&lt;/b&gt; It writes &lt;code&gt;&amp;lt;script type="module"&amp;gt;&lt;/code&gt;, then immediately references &lt;code&gt;document.currentScript&lt;/code&gt; in the first line. &lt;code&gt;currentScript&lt;/code&gt; doesn't work in modules! This is literally on the MDN page in a yellow damn warning box or something. You read the entire internet and somehow skipped that part.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;2.&lt;/b&gt; It uses my &lt;code&gt;_id("something")&lt;/code&gt; shorthand to grab elements that &lt;i&gt;don't exist in the HTML&lt;/i&gt;. Just confidently references imaginary DOM nodes. How about reading that 100 LOC partial file first? Or use null-chaining maybe? The very thing I prompted you to use because I knew this would happen? Nnnn-ope.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;3.&lt;/b&gt; On the rare occasion it did realize &lt;code&gt;getElementById&lt;/code&gt; can return null (congrats), it wrote this beauty: &lt;code&gt;onclick='var x=document.getElementById("blah"); if (x) x.doSomething();'&lt;/code&gt; (yeah, "onclick" is another story) - instead of just &lt;code&gt;_id("blah")?.doSomething()&lt;/code&gt;. You know, the thing I asked for. Never happened.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;4.&lt;/b&gt; It writes selectors like &lt;code&gt;querySelectorAll("#123")&lt;/code&gt;. CSS selectors don't allow IDs starting with a digit. Jquery did. CSS doesn't. Every junior frontend dev who's ever dealt with auto-generated IDs has been burned by this. It's a rite of passage. The AI skipped the initiation.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;5.&lt;/b&gt; Calling functions from external &lt;code&gt;&amp;lt;script type="defer"&amp;gt;&lt;/code&gt; in inline scripts without waiting for DOMContentLoaded (so that the deferred scripts can finish loading)&lt;/p&gt;

&lt;p&gt;etc...&lt;/p&gt;

&lt;img src="https://www.jitbit.com/uploads/6cAdx8L.png"&gt;

&lt;p style="text-align:center"&gt;&lt;i&gt;Yeah... Why not, bro... Good idea.&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Not to mention way more nuanced issues. For example, when you append AJAX-loaded HTML with script-tags into your document using &lt;code&gt;$.html()&lt;/code&gt; - Jquery automagically runs all the "ready" functions for you - the &lt;code&gt;$(function)&lt;/code&gt; shorthand. When you insert AJAX-ed HTML via vanilla JS &lt;code&gt;createContextualFragment&lt;/code&gt; or &lt;code&gt;createElement&lt;/code&gt; - it also executes all scripts, but DOMContentLoaded is skipped.&lt;/p&gt;

&lt;p&gt;I know, hard to catch even for a senior dev, no complaints here... Still, I had a small, tiny little hope it will spare me the load here. It wrote that Rust compiler after all.&lt;/p&gt;

&lt;p&gt;Also, why not run "npm run test" at some point? We have tons of tests. I even have an integration test that crawls the entire fucking app recusrively link-by-link in a headless browser and reports on JS errors. CLAUDE.md has all the info.&lt;/p&gt;

&lt;p&gt;Should I be doing this in the early mornings EU-time, when the US is still asleep and Anthropic does not nerf their model to save on electric bill?&lt;/p&gt;

&lt;p&gt;I mean. My project is 150K lines of code. About half of that is irrelevant - C# backend, database scripts, CI/CD - it all lives in separate directories the agent didn't even need to touch. We're talking about maybe 30-40K lines of actual frontend code. Mostly HTML. 20% of that has Jquery.&lt;/p&gt;

&lt;p&gt;I don't know what to think about that "writing a Rust-based C compiler from scratch with 24 agents" &lt;a href="https://www.youtube.com/watch?v=vNeIQS9GsZ8" rel="nofollow"&gt;demo&lt;/a&gt;. Yeah. I bet that took more time prompting and babysitting than just coding the damn thing with cursor-tab. But hey, it made a great tweet.&lt;/p&gt;

&lt;img src="https://www.jitbit.com/uploads/7CqukXl.png"&gt;
&lt;p style="text-align:center"&gt;&lt;i&gt;OK, lost my temper here, I'm sorry...&lt;/i&gt;&lt;/p&gt;

&lt;h2&gt;Context rot&lt;/h2&gt;

&lt;p&gt;We all know what's going on.&lt;/p&gt;

&lt;p&gt;AI is brilliant in "clean slate" mode. Give it a blank page and it's magic. It has a perfect mental model of the thing it &lt;b&gt;just wrote&lt;/b&gt;. No surprises, no inherited decisions, no legacy weirdness. Just like us, humans, honestly - we all love green-field projects. Who doesn't? Except it's brownfield where you earn your paycheck.&lt;/p&gt;

&lt;p&gt;The moment you point it at a real, existing codebase - even a small one - everything falls apart. It hallucinates, loses track and forgets your instructions three files in. It invents elements that don't exist. It ignores constraints you spelled out in plain English, &lt;i&gt;in the prompt&lt;/i&gt;. Honestly, I'm shocked at how bad Opus 4.6 is at complex tasks, and it's the smartest AI I've seen.. "Artificial intelligence" my ass. More like "artificial confidence". We've all worked with people like that. Now we get to pay $200/month for the privilege.&lt;/p&gt;

&lt;p&gt;And that's exactly why every wannapreneur indyhacker infobiz "ship 24 startups in 12 months" bro is so thrilled with AI tools. Of course they are. They're always starting from zero. Green field, no users, no legacy, no complexity and no revenue. AI absolutely crushes that use case.&lt;/p&gt;

&lt;p&gt;But for those of us maintaining real software that real people pay for and depend on? Yeah.&lt;/p&gt;

&lt;p&gt;I guess our jobs are safe for another couple of years. Now excuse me, have to go review 67 files.&lt;/p&gt;

&lt;br&gt;&lt;br&gt;

&lt;p style="text-align:center"&gt;&lt;i&gt;*&lt;/i&gt;&lt;/p&gt;
&lt;br&gt;&lt;br&gt;

&lt;p style="text-align:center"&gt;&lt;i&gt;*&lt;/i&gt;&lt;/p&gt;
&lt;br&gt;&lt;br&gt;

&lt;p style="text-align:center"&gt;&lt;i&gt;*&lt;/i&gt;&lt;/p&gt;
&lt;br&gt;&lt;br&gt;

&lt;img src="https://www.jitbit.com/uploads/G4c1Dyj.png"&gt;

&lt;p style="text-align:center"&gt;&lt;i&gt;My AI work be like...&lt;/i&gt;&lt;/p&gt;

&lt;br&gt;&lt;br&gt;
</description>
				<pubdate>Thu, 12 Feb 2026 21:24:27 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/cursor-2-0/</link>
				<guid>https://www.jitbit.com/alexblog/cursor-2-0/</guid>
				<title>So Cursor 2.0 is out huh</title>
				<description>&lt;p&gt;Cursor 2.0 is out. Is that it? A new model and the UI I don't care to use?&lt;/p&gt;&lt;!--more--&gt;

&lt;p&gt;&lt;img src="https://www.jitbit.com/uploads/rtowuLB.png" style="width:350px" class="mx-auto"&gt;&lt;/p&gt;

&lt;p&gt;Don't get me wrong - Cursor is still the best AI editor out there. Cursor Tab is borderline magic. When your project is clean and structured, and you move between files in a logical flow, it feels like the thing's reading my mind. But I've got some issues.&lt;/p&gt;
&lt;h3&gt;1. Still stuck eight months in the past&lt;/h3&gt;
&lt;p&gt;Cursor is way behind from the upstream. &lt;em&gt;Way&lt;/em&gt; behind. The whole point of VS Code and its extensions ecosystem is the insanely active development cycle - constant updates, bug fixes, extensions that just work. Cursor doesn't seem to care.&lt;/p&gt;
&lt;p&gt;Cursor 2.0, released &lt;strong&gt;today&lt;/strong&gt;, is still based on VS Code &lt;strong&gt;1.99.3&lt;/strong&gt;, which is eight months old. That's like three JavaScript frameworks ago.&lt;/p&gt;
&lt;p&gt;Instead of keeping the core editor up to date, they keep adding shiny new "features" no one asked for. Meanwhile, people can't even install half their extensions without errors.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://www.jitbit.com/uploads/kdgHrzA.png" style="width:350px" class="mx-auto"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://www.jitbit.com/uploads/3raxNmv.png" style="width:350px" class="mx-auto"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://www.jitbit.com/uploads/Q60BlNX.png" style="width:650px" class="mx-auto"&gt;&lt;/p&gt;

&lt;p&gt;And when confronted by the community on their forum, they just silently ignore the &lt;a href="https://forum.cursor.com/t/update-vscode-version-to-latest/75154" rel="nofollow"&gt;issues&lt;/a&gt; for months and continue to add shiny new features.&lt;/p&gt;

&lt;h3&gt;2. The extension-gate&lt;/h3&gt;

&lt;p&gt;Half the extensions I use are broken thanks to Microsoft's little "you should only use this in VS Code, not in forks" move earlier this summer. You know, the one that nuked C++, C#, TypeScript, Python and a bunch of others. Not Cursor's fault, but an unfair move by Microsoft (not cool!).&lt;/p&gt;

&lt;p&gt;However, the end result still sucks. Cursor's TypeScript extension, for example, is stuck on a version from... 2023! Meanwhile, the VSCode version literally got an update yesterday. To their credit, Cursor tried to fix this by forking some of the popular extensions and publishing alternatives, but 6 months later they're all abandoned and just as outdated as the main editor.&lt;/p&gt;

&lt;p&gt;I'm also still not sure which extension registry Cursor even uses. Is it open-vsx? Their own proxy of Microsoft's VS gallery? Some mystical mixture of both? Who knows.&lt;/p&gt;

&lt;p&gt;And again - forum threads are ignored and support is ghosted. I feel like yelling into a Slack channel no one's checked since 2023.&lt;/p&gt;

&lt;h3&gt;The sad truth&lt;/h3&gt;

&lt;p&gt;Leaving aside all the other annoyances - like remapped default shortcuts (including `⌘+K`) and frequent, confusing pricing changes - Cursor is, unfortunately, &lt;b&gt;still&lt;/b&gt; the best AI-powered editor out there. It's smart, it's fast, and when it works, it's genuinely impressive.&lt;/p&gt;

&lt;p&gt;But I'm simply tired of using it for "AI only" and then switching to a "normal" editor for debugging, testing, hot-reload etc.&lt;/p&gt;

&lt;p&gt;If they keep this up - ignoring the core experience while chasing buzzwords - I'll be switching. Maybe to Zed, maybe even back to vanilla VS Code + Claude Code. At least when something breaks there, I know who to yell at.&lt;/p&gt;</description>
				<pubdate>Wed, 29 Oct 2025 19:00:05 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/321-dont-just-ban-ips---send-the-damn-abuse-report/</link>
				<guid>https://www.jitbit.com/alexblog/321-dont-just-ban-ips---send-the-damn-abuse-report/</guid>
				<title>Don't Just Ban IPs - Send the Damn Abuse Report</title>
				<description>&lt;p&gt;I just finished dealing with a Digital Ocean IP address that sent half-a-million requests to our network and this got me thinking...&lt;/p&gt;

&lt;p&gt;Remember when we used to send abuse reports? You'd spot some shady traffic - and fire off an email to the host. Fast, easy, and effective.&lt;/p&gt;

&lt;p&gt;Now? Nobody does it. We just shrug and block.&lt;/p&gt;&lt;!--more--&gt;

&lt;p&gt;Here's the thing: &lt;strong&gt;abuse reports still work&lt;/strong&gt; (shocker). I know becasue we get them all the time.&lt;/p&gt;

&lt;p&gt;If you see brute-force attempts, port scanning, spam, malicious HTTP traffic - and it's coming from a Hetzner box or a DigitalOcean droplet - don't just block the IP. &lt;strong&gt;Take 1 minute and report it.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;How to find the abuse contact with one command&lt;/h3&gt;

&lt;p&gt;Surpiringly, most "IP info" API-providers offer abuse contact info as a paid feature. After a bit of research it turned out you can still get it for free using a reverse DNS lookup, thanks to Abusix database. Here's a Bash script:&lt;/p&gt;

&lt;pre style="font-family:monospace;color: rgb(201, 209, 217); background-color: rgb(13, 17, 23); font-weight: 400; "&gt;&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;#!/bin/bash&lt;/span&gt;

&lt;span style="color: rgb(139, 148, 158); font-weight: 400;"&gt;# USAGE: ./abuse.sh 123.123.123.123&lt;/span&gt;

&lt;span style="color: rgb(255, 123, 114); font-weight: 400;"&gt;if&lt;/span&gt; [ -z &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$1&lt;/span&gt;"&lt;/span&gt; ]; &lt;span style="color: rgb(255, 123, 114); font-weight: 400;"&gt;then&lt;/span&gt;
  &lt;span style="color: rgb(255, 166, 87); font-weight: 400;"&gt;echo&lt;/span&gt; &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"Usage: &lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$0&lt;/span&gt; &amp;lt;ip_address&amp;gt;"&lt;/span&gt;
  &lt;span style="color: rgb(255, 166, 87); font-weight: 400;"&gt;exit&lt;/span&gt; 1
&lt;span style="color: rgb(255, 123, 114); font-weight: 400;"&gt;fi&lt;/span&gt;

IP_ADDRESS=&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$1&lt;/span&gt;

&lt;span style="color: rgb(139, 148, 158); font-weight: 400;"&gt;# Reverse the IP address octets. For example, "1.2.3.4" becomes "4.3.2.1"&lt;/span&gt;
REVERSED_IP=$(&lt;span style="color: rgb(255, 166, 87); font-weight: 400;"&gt;echo&lt;/span&gt; &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$IP_ADDRESS&lt;/span&gt;"&lt;/span&gt; | awk -F. &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;'{print $4"."$3"."$2"."$1}'&lt;/span&gt;)

&lt;span style="color: rgb(139, 148, 158); font-weight: 400;"&gt;# Construct the special domain name for the Abusix query.&lt;/span&gt;
QUERY_DOMAIN=&lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$REVERSED_IP&lt;/span&gt;.abuse-contacts.abusix.zone."&lt;/span&gt;

&lt;span style="color: rgb(139, 148, 158); font-weight: 400;"&gt;# Use the 'host' command to look up the TXT record for the constructed domain.&lt;/span&gt;
&lt;span style="color: rgb(139, 148, 158); font-weight: 400;"&gt;# The abuse contact is typically found within quotes in the TXT record.&lt;/span&gt;
&lt;span style="color: rgb(139, 148, 158); font-weight: 400;"&gt;# We use grep to find the quoted string and tr to remove the quotes.&lt;/span&gt;
ABUSE_CONTACT=$(host -t TXT &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$QUERY_DOMAIN&lt;/span&gt;"&lt;/span&gt; | grep -o &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;'".*"'&lt;/span&gt; | &lt;span style="color: rgb(255, 166, 87); font-weight: 400;"&gt;tr&lt;/span&gt; -d &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;'"'&lt;/span&gt;)

&lt;span style="color: rgb(139, 148, 158); font-weight: 400;"&gt;# Check if an abuse contact was found.&lt;/span&gt;
&lt;span style="color: rgb(255, 123, 114); font-weight: 400;"&gt;if&lt;/span&gt; [ -n &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$ABUSE_CONTACT&lt;/span&gt;"&lt;/span&gt; ]; &lt;span style="color: rgb(255, 123, 114); font-weight: 400;"&gt;then&lt;/span&gt;
  &lt;span style="color: rgb(255, 166, 87); font-weight: 400;"&gt;echo&lt;/span&gt; &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"&lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$IP_ADDRESS&lt;/span&gt;: &lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$ABUSE_CONTACT&lt;/span&gt;"&lt;/span&gt;
&lt;span style="color: rgb(255, 123, 114); font-weight: 400;"&gt;else&lt;/span&gt;
  &lt;span style="color: rgb(255, 166, 87); font-weight: 400;"&gt;echo&lt;/span&gt; &lt;span style="color: rgb(165, 214, 255); font-weight: 400;"&gt;"No abuse contact found for &lt;span style="color: rgb(121, 192, 255); font-weight: 400;"&gt;$IP_ADDRESS&lt;/span&gt; via Abusix."&lt;/span&gt;
&lt;span style="color: rgb(255, 123, 114); font-weight: 400;"&gt;fi&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Save it as &lt;code&gt;abuse.sh&lt;/code&gt;, and now you're one command away from knowing who to notify.&lt;/p&gt;

&lt;h3&gt;Email template&lt;/h3&gt;

&lt;p&gt;Copy-paste this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;To: abuse@hostingprovider.com
Subject: Suspicious activity from IP [123.123.123.123]

Hello,

We've observed thousands of suspicious requests from the following IP:

- IP Address: 123.123.123.123
- Timestamp: 2025-08-01 10:23:17 UTC
- Log Excerpt:

[2025-08-01 10:23:17] GET /phpmyadmin/index.php
[2025-08-01 10:23:18] POST /wp-login.php
[2025-08-01 10:23:21] GET /.env.local
[2025-08-01 10:23:25] GET /.docker-compose.yml

This traffic is unauthorized and persistent. Please investigate.

Thanks,  
Your Name  
Your Company
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just ask your favorite AI to vibe-code the email-sending part too.&lt;/p&gt;

&lt;h3&gt;Why bother?&lt;/h3&gt;

&lt;p&gt;
According to the "2025 Imperva Bad Bot Report", 
&lt;strong&gt;51% of internet traffic is bots&lt;/strong&gt;. 
&lt;strong&gt;75% of that is malicious.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;That means most of your server logs are full of bots trying to exploit stuff.&lt;/p&gt;

&lt;p&gt;Blocking one IP at a time doesn't solve anything. But abuse reports &lt;em&gt;do&lt;/em&gt;. Cloud providers actually act on them. Some auto-suspend users after a few verified reports.&lt;/p&gt;

&lt;p&gt;
I know this firsthand - we run a SaaS app, and we occasionally get these ourselves. 
Someone registers a fake account, sends spam, and boom: we get a report from the provider before we even notice.
&lt;/p&gt;

&lt;p&gt;Cloud hosts care about abuse complaints. It's often the only signal they get about compromised droplets or customers doing shady stuff.&lt;/p&gt;

&lt;p&gt;So: don't just block. &lt;strong&gt;Report.&lt;/strong&gt; Like it's 2001 again. This takes 2 minutes, but it makes a difference.&lt;/p&gt;</description>
				<pubdate>Fri, 01 Aug 2025 12:44:11 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/320-will-ai-destroy-b2b-saas/</link>
				<guid>https://www.jitbit.com/alexblog/320-will-ai-destroy-b2b-saas/</guid>
				<title>Will AI destroy B2B SaaS?</title>
				<description>&lt;p&gt;
  Everyone worries about AI taking developer jobs, but what if AI wipes out the entire off-the-shelf software industry?
&lt;/p&gt;

&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;
      &lt;strong&gt;The "build vs. buy" equation has flipped.&lt;/strong&gt;
      Businesses used to buy SaaS because it was cheaper than building their own. AI has changed that—building your own is now more affordable than ever.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;
      &lt;strong&gt;The discovery problem.&lt;/strong&gt;
      AI recommendations default to well-established solutions. Think SEO is a long game? Try LLM SEO.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;!--more--&gt;
&lt;hr&gt;

&lt;h3&gt;The "Why Buy?" Problem&lt;/h3&gt;
&lt;p&gt;
  Six months ago, we needed an AI-powered code review tool. We explored several options and ultimately "vibe-coded" our own app that lives in a GitHub action, takes a &lt;code&gt;git log&lt;/code&gt;, sends it to Claude via the API, grabs the response and posts the results to our team's Slack. Done. The best part? AI wrote the entire thing &lt;i&gt;faster&lt;/i&gt; than it would take to sign up for a SaaS.
&lt;/p&gt;
&lt;img src="https://www.jitbit.com/uploads/AenSN1q.png" style="max-width:400px"&gt;
&lt;p&gt;
  How long until every company realizes they can do this?
&lt;/p&gt;
&lt;p&gt;
  Need a simple "CRUD" CRM, but with JIRA-style tasks? Done. Need a mobile time-tracking app for remote employees, but with GPS and a bar code reader? AI will spit out a React Native iOS build in minutes. Why pay for yet another SaaS when you can "vibe-code" something in a week?
&lt;/p&gt;
&lt;p&gt;And mark my words, LLM providers are one step away from actually hosting the code they generate. Who needs to spawn an AWS server if you can just ask OpenAI to run the code it just wrote?&lt;/p&gt;

&lt;blockquote&gt;- "Hey Siri! build me a Basecamp, but with green buttons, also register a domain, spawn a server and host it all there, charge this credit card when you're done"&lt;br&gt;&lt;br&gt;
- "Absolutely, that'd be $1.17 per hour"&lt;/blockquote&gt;

&lt;h3&gt;The Discovery Problem&lt;/h3&gt;
&lt;p&gt;
  AI doesn’t just make it easier to build software—it makes it harder for new SaaS products to get discovered. When you ask AI for recommendations, it defaults to the biggest names.
&lt;/p&gt;
&lt;p&gt;
  And not just in SaaS, by the way, in open source too. Imagine launching a killer new JS framework today. AI coding assistants and tools like Cursor will just default to React anyway. And not even the latest version of it! In a recent tweet Adam Wathan, the creator of Tailwind, asked: "Has anyone migrated to Tailwind 4.0 yet?"
&lt;/p&gt;
&lt;p&gt; The most popular response was "Nah! we're still waiting for LLMs to learn it."&lt;/p&gt;
&lt;p&gt;
  AI isn’t just "the next internet moment." It’s more like "the social network moment." Echo chambers get louder, big names get bigger, and smaller ones disappear into the noise.
&lt;/p&gt;

&lt;h3&gt;What Can SaaS Companies Do?&lt;/h3&gt;
&lt;h4&gt;1. Become an Industry Standard&lt;/h4&gt;
&lt;p&gt;
  Or at least a "go-to" product in a niche. If your app becomes something people mention on their CVs or job descriptions, you win. Examples: Slack. HubSpot. Salesforce etc. A salesperson moving to a new company simply &lt;i&gt;expects&lt;/i&gt; Salesforce to be there. That kind of lock-in ensures survival.
&lt;/p&gt;

&lt;h4&gt;2. Build Moats: Infrastructure &amp; Vendor Lock-In&lt;/h4&gt;
&lt;p&gt;
  Simple CRUD apps will just die. The ones that survive will own infrastructure or at least some part of it. Instead of building another AI voice assistant, create one &lt;i&gt;with built-in VoIP and provide landline numbers to customers&lt;/i&gt;.
&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;
      &lt;strong&gt;Transistor.fm&lt;/strong&gt; – Not just a SaaS, but also a podcast hosting and publishing pipeline.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;
      &lt;strong&gt;Postmark&lt;/strong&gt; (or any transactional email service really) – yes, AI can code an email-sending app, but it can't get you a 10-year old high-reputation sender IP address trusted by Gmail and Outlook.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;
      &lt;b&gt;SignWell&lt;/b&gt; (online document sign), &lt;b&gt;SavvyCal&lt;/b&gt; (Calendly replacement) and similar "inter-business" file-sharing &amp; escrow apps that act as intermediaries, owning some part of the communication channel. Those are literally easier to use than bother vibe-coding a custom alternative. However, these apps are so trivial to clone, that success comes down to who’s the better marketer.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Which SaaS Will Die First?&lt;/h3&gt;
&lt;p&gt;
  Side-project-scale, "one simple tool" SaaS products that used to be easy wins—form builders, schedulers, basic dashboards, simple workflow apps—those days are over. Even bigger apps but in a well-researched field, like project management or (cough-cough) helpdesk software are in danger. If AI can generate it in an afternoon, no one is paying a subscription for it. Oh, and "no code" is toasted obviously.
&lt;/p&gt;
&lt;p&gt;
  The SaaS graveyard is about to get a lot more crowded. I give it 4 years.&lt;/p&gt;
  
&lt;p&gt;Software consulting is making a comeback though. Someone has to clean up the vibe-coded chaos.&lt;/p&gt;</description>
				<pubdate>Fri, 07 Mar 2025 10:14:05 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/zen-review-benchmark/</link>
				<guid>https://www.jitbit.com/alexblog/zen-review-benchmark/</guid>
				<title>Zen Browser review and benchmark vs Chrome, Brave, Firefox and Safari</title>
				<description>&lt;p&gt;
	I'm looking for a new daily driver browser on my Mac. Chrome is a non-starter for me due to privacy concerns (Google's tracking empire is alive and well), and Edge is just... too much. Every update shoves another set of “features” down my throat — Copilot, discount coupons, Bing nonsense — things I have to disable again and again. No thanks.
&lt;/p&gt;&lt;!--more--&gt;
&lt;p&gt;
	I currently use Brave and I really want to like it, but something about it doesn't sit right with me. The constant crypto integration,
		some of the decisions around their search engine — it just feels like it's got an agenda. Arc? Well, Arc is dying
		now, so that's out.&lt;/p&gt;
&lt;p&gt;Someone suggested Zen, which is a Firefox-based browser aiming to be an Arc-like alternative. That got me curious.
&lt;/p&gt;
&lt;p&gt;
	And since I already had all these browsers installed, I figured: why not run some benchmarks and see how they stack up?
&lt;/p&gt;
&lt;h3&gt;
	Benchmark Setup
&lt;/h3&gt;
&lt;p&gt;
	All tests were run using Speedometer 3.0 on a MacBook M3 Pro. I tested in incognito/private mode with (almost) no extensions:
&lt;/p&gt;
&lt;ul data-spread="false"&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Chrome&lt;/strong&gt;: Running uBlock Origin
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Brave&lt;/strong&gt;: Default built-in ad/privacy blocker enabled
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Safari&lt;/strong&gt;: Clean
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Firefox&lt;/strong&gt;: Clean
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Zen&lt;/strong&gt;: Clean
		&lt;/p&gt;
	&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
	Results
&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Chrome 132.0.6834.160&lt;/strong&gt;
			- 
			&lt;em&gt;37.7&lt;/em&gt;
			
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Brave 1.74.51&lt;/strong&gt;
			- 
			&lt;em&gt;37.6&lt;/em&gt;
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Safari 18.2&lt;/strong&gt;
			- 
			&lt;em&gt;37.6&lt;/em&gt;
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Firefox 134.0.2&lt;/strong&gt;
			- 
			&lt;em&gt;34.8&lt;/em&gt;
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			&lt;strong&gt;Zen Browser 1.7.3b&lt;/strong&gt;
			- 
			&lt;em&gt;31.6&lt;/em&gt;
		&lt;/p&gt;
	&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
    &lt;svg version="1.1" style="font-family: Roboto; font-size: 12px;max-width:100%" xmlns="http://www.w3.org/2000/svg" width="650" height="400" viewbox="0 0 650 400" aria-hidden="false" aria-label="Interactive chart"&gt;&lt;defs aria-hidden="true"&gt;&lt;clippath&gt;&lt;rect x="0" y="0" width="281" height="554" fill="none"&gt;&lt;/rect&gt;&lt;/clippath&gt;&lt;clippath&gt;&lt;rect x="0" y="0" width="281" height="554" fill="none"&gt;&lt;/rect&gt;&lt;/clippath&gt;&lt;/defs&gt;&lt;rect fill="#ffffff" x="0" y="0" width="650" height="400" rx="0" ry="0" aria-hidden="true"&gt;&lt;/rect&gt;&lt;rect fill="none" x="86" y="46" width="554" height="281" aria-hidden="true"&gt;&lt;/rect&gt;&lt;g data-z-index="0" aria-hidden="true"&gt;&lt;/g&gt;&lt;rect fill="none" data-z-index="1" x="86" y="46" width="554" height="281" aria-hidden="true"&gt;&lt;/rect&gt;&lt;g data-z-index="1" aria-hidden="true"&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 86 102.5 L 640 102.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 86 158.5 L 640 158.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 86 215.5 L 640 215.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 86 271.5 L 640 271.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 86 327.5 L 640 327.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 86 46.5 L 640 46.5" opacity="1"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="1" aria-hidden="true"&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 85.5 46 L 85.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 154.5 46 L 154.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 224.5 46 L 224.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 293.5 46 L 293.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 362.5 46 L 362.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 431.5 46 L 431.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 501.5 46 L 501.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 570.5 46 L 570.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 640.5 46 L 640.5 327" opacity="1"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="2" aria-hidden="true"&gt;&lt;path fill="none" stroke="#ccd6eb" stroke-width="1" data-z-index="7" d="M 85.5 46 L 85.5 327"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="2" aria-hidden="true"&gt;&lt;path fill="none" data-z-index="7" d="M 86 327 L 640 327"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="3" aria-hidden="false"&gt;&lt;g data-z-index="0.1" opacity="1" transform="translate(640,327) rotate(90) scale(-1,1) scale(1 1)" clip-path="url(#highcharts-ejw35qy-105-)" style="cursor: pointer;" aria-hidden="false" width="554" height="281"&gt;&lt;rect x="243" y="33" width="21" height="522" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Chome, 37.7. Speedometer." style="outline: none;"&gt;&lt;/rect&gt;&lt;rect x="187" y="34" width="21" height="521" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Brave, 37.6. Speedometer." style="outline: none;"&gt;&lt;/rect&gt;&lt;rect x="130" y="34" width="21" height="521" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Safari, 37.6. Speedometer." style="outline: none;"&gt;&lt;/rect&gt;&lt;rect x="74" y="73" width="21" height="482" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Firefox, 34.8. Speedometer." style="outline: none;"&gt;&lt;/rect&gt;&lt;rect x="18" y="117" width="21" height="438" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Zen Browser, 31.6. Speedometer." style="outline: none;"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g data-z-index="0.1" opacity="1" transform="translate(640,327) rotate(90) scale(-1,1) scale(1 1)" clip-path="none" aria-hidden="true"&gt;&lt;/g&gt;&lt;/g&gt;&lt;text font-family="Roboto-Regular,Roboto" x="325" text-anchor="middle" data-z-index="4" style="color: rgb(51, 51, 51); font-size: 18px; fill: rgb(51, 51, 51);" y="24" aria-hidden="true"&gt;Browser bench&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="325" text-anchor="middle" data-z-index="4" style="color: rgb(102, 102, 102); fill: rgb(102, 102, 102);" y="45" aria-hidden="true"&gt;&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="10" text-anchor="start" data-z-index="4" style="color: rgb(102, 102, 102); fill: rgb(102, 102, 102);" y="397" aria-hidden="true"&gt;&lt;/text&gt;&lt;g data-z-index="7" aria-hidden="true" transform="translate(271,359)"&gt;&lt;rect fill="none" rx="0" ry="0" x="0" y="0" width="108" height="26"&gt;&lt;/rect&gt;&lt;g data-z-index="1"&gt;&lt;g&gt;&lt;g data-z-index="1" transform="translate(8,3)"&gt;&lt;text font-family="Roboto-Regular,Roboto" x="21" text-anchor="start" data-z-index="2" y="15" style="color: rgb(51, 51, 51); cursor: pointer; font-size: 12px; font-weight: bold; fill: rgb(51, 51, 51);"&gt;Speedometer score (higher is better)&lt;/text&gt;&lt;rect x="2" y="4" width="12" height="12" fill="#6f58e9" rx="6" ry="6" data-z-index="3"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-z-index="7" aria-hidden="true"&gt;&lt;text font-family="Roboto-Regular,Roboto" x="71" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="79" opacity="1"&gt;Chome&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="71" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="135" opacity="1"&gt;Brave&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="71" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="191" opacity="1"&gt;Safari&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="71" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="247" opacity="1"&gt;Firefox&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="71" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="304" opacity="1"&gt;Zen Browser&lt;/text&gt;&lt;/g&gt;&lt;g data-z-index="7" aria-hidden="true"&gt;&lt;text font-family="Roboto-Regular,Roboto" x="86" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="middle" transform="translate(0,0)" y="346" opacity="1"&gt;0&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="155.25" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="middle" transform="translate(0,0)" y="346" opacity="1"&gt;5&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="224.5" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="middle" transform="translate(0,0)" y="346" opacity="1"&gt;10&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="293.75" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;15&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="363" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;20&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="432.25" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;25&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="501.5" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;30&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="570.75" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;35&lt;/text&gt;&lt;text font-family="Roboto-Regular,Roboto" x="633.81640625" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;40&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;/p&gt;
&lt;p&gt;
	A few takeaways:
&lt;/p&gt;
&lt;ul data-spread="false"&gt;
	&lt;li&gt;
		&lt;p&gt;
			Chrome is (unsurprisingly) the fastest. Even though I forgot to remove the UBO extension in incognito.
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			Brave is essentially Chrome with a privacy skin, Leo AI, some Crypto stuff etc, and the Speedometer score reflects that.
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			Safari holds up impressively well, matching Brave's performance while being more native to macOS (and using less battery). I wish it had &lt;i&gt;proper&lt;/i&gt; vertical tabs.
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			Firefox holds up well but is still behind Chromium-based browsers. Not awful, but not amazing either.
		&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;p&gt;
			Zen, being Firefox-based, lags a bit further behind. If you want a Firefox alternative that looks
				different but runs about the same, it's an option. Otherwise, it's just Firefox with extra UI features (see below).
		&lt;/p&gt;
	&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
    Now here's RAM usage after 3 hours of "normal" browsing: watching Youtube, chatting in Slack, reading Gmail, 8-9 tabs overall, with all extensions enabled:
&lt;/p&gt;
&lt;p&gt;
    &lt;svg version="1.1" style="font-family: Roboto; font-size: 12px;" xmlns="http://www.w3.org/2000/svg" width="650" height="400" viewbox="0 0 650 400" aria-hidden="false" aria-label="Interactive chart"&gt;&lt;defs aria-hidden="true"&gt;&lt;clippath&gt;&lt;rect x="0" y="0" width="281" height="576" fill="none"&gt;&lt;/rect&gt;&lt;/clippath&gt;&lt;clippath&gt;&lt;rect x="0" y="0" width="281" height="576" fill="none"&gt;&lt;/rect&gt;&lt;/clippath&gt;&lt;/defs&gt;&lt;rect fill="#ffffff" x="0" y="0" width="650" height="400" rx="0" ry="0" aria-hidden="true"&gt;&lt;/rect&gt;&lt;rect fill="none" x="64" y="46" width="576" height="281" aria-hidden="true"&gt;&lt;/rect&gt;&lt;g data-z-index="0" aria-hidden="true"&gt;&lt;/g&gt;&lt;rect fill="none" data-z-index="1" x="64" y="46" width="576" height="281" aria-hidden="true"&gt;&lt;/rect&gt;&lt;g data-z-index="1" aria-hidden="true"&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 64 116.5 L 640 116.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 64 187.5 L 640 187.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 64 257.5 L 640 257.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 64 327.5 L 640 327.5" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke-dasharray="none" data-z-index="1" d="M 64 46.5 L 640 46.5" opacity="1"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="1" aria-hidden="true"&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 63.5 46 L 63.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 145.5 46 L 145.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 228.5 46 L 228.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 310.5 46 L 310.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 392.5 46 L 392.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 474.5 46 L 474.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 557.5 46 L 557.5 327" opacity="1"&gt;&lt;/path&gt;&lt;path fill="none" stroke="#e6e6e6" stroke-width="1" stroke-dasharray="none" data-z-index="1" d="M 640.5 46 L 640.5 327" opacity="1"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="2" aria-hidden="true"&gt;&lt;path fill="none" stroke="#ccd6eb" stroke-width="1" data-z-index="7" d="M 63.5 46 L 63.5 327"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="2" aria-hidden="true"&gt;&lt;path fill="none" data-z-index="7" d="M 64 327 L 640 327"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-z-index="3" aria-hidden="false"&gt;&lt;g data-z-index="0.1" opacity="1" transform="translate(640,327) rotate(90) scale(-1,1) scale(1 1)" clip-path="url(#highcharts-lbz6rv1-120-)" style="cursor: pointer;" aria-hidden="false" width="576" height="281"&gt;&lt;rect x="233" y="100" width="26" height="477" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Brave, 2.9. GB." style="outline: none;"&gt;&lt;/rect&gt;&lt;rect x="163" y="166" width="26" height="411" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Chrome, 2.5. GB." style="outline: none;"&gt;&lt;/rect&gt;&lt;rect x="93" y="149" width="26" height="428" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Zen, 2.6. GB." style="outline: none;"&gt;&lt;/rect&gt;&lt;rect x="22" y="281" width="26" height="296" fill="#6f58e9" opacity="1" tabindex="-1" role="img" aria-label="Safari, 1.8. GB." style="outline: none;"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g data-z-index="0.1" opacity="1" transform="translate(640,327) rotate(90) scale(-1,1) scale(1 1)" clip-path="none" aria-hidden="true"&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-z-index="3" aria-hidden="true"&gt;&lt;g stroke-linecap="round" style="cursor: pointer;" transform="translate(616,10)"&gt;&lt;title&gt;Chart context menu&lt;/title&gt;&lt;rect fill="#ffffff" x="0.5" y="0.5" width="24" height="22" rx="2" ry="2" stroke="none" stroke-width="1"&gt;&lt;/rect&gt;&lt;path fill="#666666" d="M 6 6.5 L 20 6.5 M 6 11.5 L 20 11.5 M 6 16.5 L 20 16.5" data-z-index="1" stroke="#666666" stroke-width="3"&gt;&lt;/path&gt;&lt;text font-family='Roboto-Regular,Roboto' x="0" data-z-index="1" y="15.5" style="color: rgb(51, 51, 51); font-weight: normal; fill: rgb(51, 51, 51);"&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;text font-family='Roboto-Regular,Roboto' x="325" text-anchor="middle" data-z-index="4" style="color: rgb(51, 51, 51); font-size: 18px; fill: rgb(51, 51, 51);" y="24" aria-hidden="true"&gt;Chart title&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="325" text-anchor="middle" data-z-index="4" style="color: rgb(102, 102, 102); fill: rgb(102, 102, 102);" y="45" aria-hidden="true"&gt;&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="10" text-anchor="start" data-z-index="4" style="color: rgb(102, 102, 102); fill: rgb(102, 102, 102);" y="397" aria-hidden="true"&gt;&lt;/text&gt;&lt;g data-z-index="7" aria-hidden="true" transform="translate(299,359)"&gt;&lt;rect fill="none" rx="0" ry="0" x="0" y="0" width="52" height="26"&gt;&lt;/rect&gt;&lt;g data-z-index="1"&gt;&lt;g&gt;&lt;g data-z-index="1" transform="translate(8,3)"&gt;&lt;text font-family='Roboto-Regular,Roboto' x="21" text-anchor="start" data-z-index="2" y="15" style="color: rgb(51, 51, 51); cursor: pointer; font-size: 12px; font-weight: bold; fill: rgb(51, 51, 51);"&gt;GB&lt;/text&gt;&lt;rect x="2" y="4" width="12" height="12" fill="#6f58e9" rx="6" ry="6" data-z-index="3"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-z-index="7" aria-hidden="true"&gt;&lt;text font-family='Roboto-Regular,Roboto' x="49" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="86" opacity="1"&gt;Brave&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="49" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="156" opacity="1"&gt;Chrome&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="49" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="226" opacity="1"&gt;Zen&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="49" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="end" transform="translate(0,0)" y="297" opacity="1"&gt;Safari&lt;/text&gt;&lt;/g&gt;&lt;g data-z-index="7" aria-hidden="true"&gt;&lt;text font-family='Roboto-Regular,Roboto' x="64" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="middle" transform="translate(0,0)" y="346" opacity="1"&gt;0&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="146.28571428571" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" text-anchor="middle" transform="translate(0,0)" y="346" opacity="1"&gt;0.5&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="228.57142857143" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;1&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="310.85714285714" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;1.5&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="393.14285714286" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;2&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="475.42857142857" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;2.5&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="557.71428571429" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;3&lt;/text&gt;&lt;text font-family='Roboto-Regular,Roboto' x="632.3671875" text-anchor="middle" transform="translate(0,0)" style="color: rgb(102, 102, 102); cursor: default; font-size: 11px; fill: rgb(102, 102, 102);" y="346" opacity="1"&gt;3.5&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;/p&gt;

&lt;blockquote&gt;&lt;b&gt;UPDATE&lt;/b&gt;: after publishing this post I realized that I measure RAM usage entirely wrong. Chromium-based browsers always launch a ton of processes (one browser, 16 "renderers", 5 "helpers" and a GPU process) and carefully I summed them all up. However for Zen and Firefox I only counted the main "Zen/Firefox" process, while turns out these browsers also spawn a dozen processes like "Isolated WebContent", "Utility process", "WebExtensions" - which almost double the memory load. I'll update the screenshot after another run.&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;b&gt;UPDATED 2&lt;/b&gt;: fixed!&lt;/blockquote&gt;

&lt;h3&gt;
	A (tiny) Zen review
&lt;/h3&gt;
&lt;p&gt;
    Zen is a very, very nice browser, but it has some rough edges:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;(nitpicking) Lacks standard macOS keyboard shortcuts — for example, &lt;code&gt;Cmd+W&lt;/code&gt; should close a window when no tabs are left. There's a hidden setting &lt;code&gt;browser.tabs.closeWindowWithLastTab
&lt;/code&gt; to fix this, but seriously, just follow macOS conventions by default. Took me a while to find.&lt;/li&gt;
    &lt;li&gt;No built-in adblocker, have to install uBlock Origin like it's 2023 again (kidding).&lt;/li&gt;
    &lt;li&gt;The dev tools are Firefox-based, and that says it all. JavaScript debugging is flaky (unreliable variable watch list, breakpoints sometimes get skipped), and reverse-engineering complex CSS can be a nightmare.
    &lt;li&gt;That said, Zen a &lt;i&gt;very&lt;/i&gt; solid contender, and some of its UI design choices are genuinely great! If you'd like to learn more watch &lt;a href="https://www.youtube.com/watch?v=dPUzOQdUFSg" target=_blank rel="nofollow"&gt;Theo's review&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
	P.S. 1Password Is a Performance Killer
&lt;/h3&gt;
&lt;p&gt;
	One of the most surprising findings was how much 1Password's extension &lt;em&gt;destroys&lt;/em&gt; Speedometer scores. Across all browsers, enabling it dropped my score by &lt;strong&gt;10 points&lt;/strong&gt;. No clue what it's doing under the hood, but it's heavy. Probably scans all inputs to shove a password into.
&lt;/p&gt;</description>
				<pubdate>Fri, 31 Jan 2025 09:28:34 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/deepseek/</link>
				<guid>https://www.jitbit.com/alexblog/deepseek/</guid>
				<title>No, Wall Street, DeepSeek is not "far superior"</title>
				<description>&lt;p&gt;&lt;b&gt;I mean, it is!&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;But the whole story about the stock market reacting to the news about DeepSeek V3 and R1 is a fine example of the knee-jerk nature of mass consciousness in the era of clickbait economics.&lt;/p&gt;&lt;!--more--&gt;

&lt;p&gt;Briefly, by points:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;No, DeepSeek isn’t “head and shoulders above” every other model.&lt;/strong&gt; The results vary across benchmarks, but on average, GPT-4o and Gemini-2 are better. You can see this on ChatBot Arena, for example (&lt;a href="https://www.reddit.com/r/LocalLLaMA/comments/1i8u9jk/deepseekr1_appears_on_lmsys_arena_leaderboard/" rel="nofollow"&gt;Reddit thread&lt;/a&gt;). Even in the results published by DeepSeek’s authors themselves (&lt;a href="https://github.com/deepseek-ai/DeepSeek-V3/blob/main/figures/benchmark.png" rel="nofollow"&gt;benchmark graph&lt;/a&gt;), you can see that in several tests, the model lags behind GPT-4o from May 2024—which, mind you, is currently ranked 16th on ChatBot Arena.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;No, training DeepSeek didn’t cost $6 million, “100 times less than GPT-4.”&lt;/strong&gt; The $6 million figure refers only to the final training run of the published model. It doesn’t include any prior experiments, earlier versions, or R&amp;amp;D costs. This is just the raw computational cost of that final training run. And guess what? That figure is pretty much in line with models of the same class.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;No, Nvidia did not deserve this hit&lt;/strong&gt; Not that we’re shedding tears for them — they could use a push to lower hardware prices. And let's not forget that DeepSeek was still trained on Nvidia’s own hardware. And no, their GPUs aren’t suddenly obsolete. DeepSeek’s computational budget is fairly standard for training, and inference for such a massive model (reminder: it’s an MoE with 671 billion parameters, 37 billion of which are active per token generation) requires a ton of hardware. Inference costs are roughly on par with a 70B dense model. Naturally, they’ll scale this success by throwing even more hardware at it and making the model bigger.&lt;/p&gt;

    &lt;p&gt;Not to mention that Deepseek makes LLMs more accessible for the on-prem customers. Which means smaller businesses will buy more GPU's, which is still good for NVDA, am I right?&lt;/p&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Does this mean the model is bad? No, the model is very, VERY good.&lt;/strong&gt; It outperforms the vast majority of open-source models, which is fantastic. DeepSeek used 8-bit floating point numbers (FP8) throughout the entire training process. This sacrifices some of that precision to save memory and boost performance. Additionally, they employed a multi-token prediction system and innovative GPU clustering/connectivity techniques. These are clever and practical engineering choices that undoubtedly contributed to their success.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;


&lt;p&gt;In the end, though, stocks will recover, ideas will spread, models will get better, and progress will march on (hopefully).&lt;/p&gt;</description>
				<pubdate>Tue, 28 Jan 2025 11:50:28 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/317-im-finally-dumping-visual-studio/</link>
				<guid>https://www.jitbit.com/alexblog/317-im-finally-dumping-visual-studio/</guid>
				<title>I'm finally dumping Visual Studio</title>
				<description>&lt;p&gt;
	I've been a C# developer for 20 years and I've had enough. After two decades of working with the "big" Visual Studio, I'm effing fed up.
	It's buggy, slow, and frustrating, and I've decided to make the switch to Visual Studio Code which I only used occasionally until now.
	While I'm still unsure if I can replicate every aspect of my workflow in VS Code,
	I'm willing to give it a shot — and so far, I'm &lt;i&gt;really&lt;/i&gt; impressed.
&lt;/p&gt;&lt;!--more--&gt;

&lt;h3&gt;1. Performance&lt;/h3&gt;
&lt;p&gt;
	Visual Studio 2022 performance has been a constant issue.
	It's sluggish and feels increasingly bloated with every new update.
	It's like watching paint dry every time I open a project.
	In contrast, Visual Studio Code feels lightweight and incredibly fast. It does use more memory (obviously) but everything is oh so snappy.
	The first time I opened my large project in VS Code, I was shocked — it loaded in lees than a second, literally,
	even with extensions like "&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp" rel="nofollow"&gt;C#&lt;/a&gt;" and "&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit" rel="nofollow"&gt;C# Dev Kit&lt;/a&gt;" installed.
&lt;/p&gt;

&lt;h3&gt;2. Better Developer Experience&lt;/h3&gt;
&lt;p&gt;
	Running &lt;code&gt;dotnet watch run&lt;/code&gt; in VS Code's terminal has been a revelation.
	It's fast, responsive, and actually works consistently.
	Visual Studio's "hot reload" feature, on the other hand, has been a constant source of frustration for me.
	Half the time it doesn't work, and I'm left restarting debugging sessions over and over again.
	I can't tell you how many hours I've lost to that unreliable feature.
&lt;/p&gt;
&lt;p&gt;
    Not to mention other VSCode's handy features, like undo/redo history even after reopening a file, as well as remembering cursor positions and selections, command palette, zen-mode, settings-sync, split editor, built-in terminal etc.
&lt;/p&gt;
&lt;h3&gt;3. Fewer Bugs, Less Frustration&lt;/h3&gt;
&lt;p&gt;
	The minor editor bugs in Visual Studio have been endless and exhausting.
	I remember one particularly infuriating bug where syntax highlighting would break in Razor and &lt;code&gt;.cshtml&lt;/code&gt; files whenever I used certain HTML tags or even just adjusted the indentation.
	It drove me up the wall! Not to mention the bizarre issues with JavaScript formatting that never seemed to get fixed.
	Since switching to VS Code, I've encountered far fewer bugs.
	It just feels like an environment that respects my time and sanity.
	&lt;img src="https://www.jitbit.com/uploads/ejG3VPL.png"&gt;
&lt;/p&gt;

&lt;h3&gt;4. A Thriving Ecosystem&lt;/h3&gt;
&lt;p&gt;
	The VS Code extension ecosystem is alive and thriving.
	Need Tailwind CSS IntelliSense? There's an &lt;a href="https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss" rel="nofollow"&gt;extension&lt;/a&gt; for that, and it works beautifully.
	Want to visualize your Git history &lt;i&gt;for a particular line&lt;/i&gt; (better version of git-blame)?
	The &lt;a href="https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistory" rel="nofollow"&gt;Git History&lt;/a&gt; extension has got you covered.
	In "big" Visual Studio, I'd report issues through the "feedback hub" and wait months — or even years — for a response.
	With VS Code, the community is constantly contributing new tools and improvements.
	It's energizing (and sometimes exhausting) to be part of such an active ecosystem.
&lt;/p&gt;
&lt;h3&gt;5. Cross-Platform Flexibility&lt;/h3&gt;
&lt;p&gt;
	One of the biggest advantages I've found with Visual Studio Code is its true cross-platform support.
	Whether I'm on my Windows PC gaming rig at home or my MacBook while traveling, VS Code runs smoothly and keeps my workflow consistent.
	Visual Studio's limited macOS version just doesn't cut it for me.
	Being able to switch between machines without missing a beat has been a game-changer.
&lt;/p&gt;
&lt;h3&gt;But... but... it's Electron!&lt;/h3&gt;
&lt;p&gt;
	I have to admit, I was skeptical at first.
	I've always had a bit of a grudge against Electron-based apps — they've often felt sluggish and bloated. Yeah, I'm one those people who run Slack and Discord in a browser.
	But VS Code has completely changed my perspective.
	It's fast, responsive, and flexible enough to let me build the development environment that works best for me.
	Switching to VS Code has rekindled my passion for coding; it reminds me why I fell in love with development in the first place.
	While Visual Studio will always have its strengths, I need a tool that evolves with me — not one that holds me back.
&lt;/p&gt;
&lt;h3&gt;P.S. Bonus tip for C# devs&lt;/h3&gt;
&lt;p&gt;
    Here's an instant fix for VSCode's high memory usage when working with C# projects, for those who see several gigs or RAM eaten by &lt;code&gt;Microsoft.CodeAnalysis.LanguageServer&lt;/code&gt; just uncheck this box:
    &lt;img src="https://www.jitbit.com/uploads/gsd4SRG.png"&gt;
&lt;/p&gt;</description>
				<pubdate>Tue, 17 Sep 2024 10:40:06 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/316-please-stop-using-any-for-c-lists-and-arrays/</link>
				<guid>https://www.jitbit.com/alexblog/316-please-stop-using-any-for-c-lists-and-arrays/</guid>
				<title>Please stop using Any() for C# Lists and Arrays</title>
				<description>  &lt;p&gt;
    When it comes to performance in C#, even small inefficiencies can add up in large codebases. One common example is the use of the LINQ method &lt;code&gt;.Any()&lt;/code&gt; on arrays and lists instead of directly checking &lt;code&gt;.Length&lt;/code&gt; or &lt;code&gt;.Count&lt;/code&gt;. While &lt;code&gt;.Any()&lt;/code&gt; improves readability in some contexts, it introduces measurable overhead when used with collections that already expose a count property.
  &lt;/p&gt;
&lt;!--more--&gt;
  &lt;h2&gt;Benchmark Setup&lt;/h2&gt;

  &lt;p&gt;
    Using BenchmarkDotNet, I compared &lt;code&gt;.Any()&lt;/code&gt; with &lt;code&gt;.Length &amp;gt; 0&lt;/code&gt; for a 100-element integer array. The difference was significant.
  &lt;/p&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Method&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;Error&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;StdDev&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Any&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;5.5362 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;1.0495 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.0575 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;-&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Length&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.2413 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.1316 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.0072 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;-&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;p&gt;
    As shown above, checking &lt;code&gt;.Length&lt;/code&gt; is roughly 30 times faster than using &lt;code&gt;.Any()&lt;/code&gt; on an array. The reason is simple: &lt;code&gt;.Length&lt;/code&gt; is a direct property access, while &lt;code&gt;.Any()&lt;/code&gt; adds the overhead of an enumerator.
  &lt;/p&gt;

  &lt;h2&gt;Benchmark with Lists&lt;/h2&gt;

  &lt;p&gt;
    The same comparison was performed using a &lt;code&gt;List&amp;lt;int&amp;gt;&lt;/code&gt;. The results are even more striking.
  &lt;/p&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Method&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;Error&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;StdDev&lt;/th&gt;
        &lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Any&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;2.3934 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.0325 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.0018 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;-&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Count&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.0000 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.0000 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;0.0000 ns&lt;/td&gt;
        &lt;td style="text-align: right;"&gt;-&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;p&gt;
    For lists, &lt;code&gt;.Count&lt;/code&gt; is effectively instantaneous in this benchmark context - it’s a property access resolved at compile time. In contrast, &lt;code&gt;.Any()&lt;/code&gt; must still perform a method call and a single enumerator step.
  &lt;/p&gt;

  &lt;h2&gt;Why &lt;code&gt;.Any()&lt;/code&gt; Is Slower&lt;/h2&gt;

  &lt;p&gt;
    The LINQ implementation of &lt;code&gt;.Any()&lt;/code&gt; does have some optimization routes (if the source list implements &lt;code&gt;ICollection&lt;/code&gt; it's smart enough to detect that, but the overhead is still there. And for everything else it simply obtains an enumerator and calls &lt;code&gt;.MoveNext()&lt;/code&gt; once to check for the presence of elements. This abstraction introduces a small but measurable overhead compared to directly checking &lt;code&gt;.Count&lt;/code&gt; or &lt;code&gt;.Length&lt;/code&gt;.
  &lt;/p&gt;

  &lt;h2&gt;Best Practice&lt;/h2&gt;

  &lt;p&gt;
    When working with &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; - especially when the underlying type is not known - &lt;code&gt;.Any()&lt;/code&gt; remains the correct and idiomatic choice. However, for arrays and lists where you can access &lt;code&gt;.Count&lt;/code&gt; or &lt;code&gt;.Length&lt;/code&gt;, prefer those properties for both clarity and performance.
  &lt;/p&gt;

  &lt;p&gt;
    In fact, Visual Studio’s code analysis tools will suggest this optimization automatically, recommending direct property checks instead of &lt;code&gt;.Any()&lt;/code&gt; when the collection type allows it.
  &lt;/p&gt;</description>
				<pubdate>Wed, 24 Jan 2024 11:19:51 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/315-clearing-nginx-proxy-cache-for-wildcard-urls-a-diy-guide/</link>
				<guid>https://www.jitbit.com/alexblog/315-clearing-nginx-proxy-cache-for-wildcard-urls-a-diy-guide/</guid>
				<title>Clearing Nginx Proxy Cache for Wildcard URLs: A DIY Guide</title>
				<description>&lt;p&gt;Sometimes you find yourself in a situation where you need to clear the Nginx proxy cache for a specific set of URLs, particularly those following a wildcard pattern like &lt;code&gt;www.website.com/folder/*&lt;/code&gt;. Surprisingly, Nginx, in its standard offering, doesn't provide a straightforward way to do this. They probably want you to upgrade to Nginx Plus for such "advanced" features. But fear not! There's a DIY workaround that's both effective and satisfyingly rebellious.&lt;/p&gt;&lt;!--more--&gt;

&lt;h2&gt;Understanding the Nginx Cache Key&lt;/h2&gt;

&lt;p&gt;First, let's dive into how Nginx generates its cache key. In my case it is:&lt;/p&gt;

&lt;pre&gt;proxy_cache_key "$scheme$request_method$host$request_uri";&lt;/pre&gt;

&lt;p&gt;It's a combination of the request schema, method, host, and path. It can be different in your case, and knowing this is crucial for our task.&lt;/p&gt;

&lt;h2&gt;The Magic Command&lt;/h2&gt;

&lt;p&gt;To clear the cache for all URLs starting with &lt;code&gt;www.website.com/folder/&lt;/code&gt;, use the following command:&lt;/p&gt;

&lt;pre&gt;sudo grep -lr "GETwww.website.com/folder/" /path/to/nginx/cache/ | sudo xargs rm&lt;/pre&gt;

&lt;h2&gt;How Does This Work?&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Grep Magic&lt;/strong&gt;: &lt;code&gt;grep -lr "GETwww.website.com/folder/" /path/to/nginx/cache/&lt;/code&gt; searches through the Nginx cache directory for files containing the string &lt;code&gt;GETwww.website.com/folder/&lt;/code&gt;. The &lt;code&gt;-l&lt;/code&gt; flag tells &lt;code&gt;grep&lt;/code&gt; to only output the names of files with matching content, and &lt;code&gt;-r&lt;/code&gt; makes the search recursive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pipe to &lt;code&gt;xargs rm&lt;/code&gt;&lt;/strong&gt;: The output (i.e., the list of cache files matching our criteria) is then piped to &lt;code&gt;xargs rm&lt;/code&gt;, which, as you might guess, removes these files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache Cleared&lt;/strong&gt;: Voilà! The cache for all URLs under &lt;code&gt;www.website.com/folder/*&lt;/code&gt; is now cleared.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;So there you have it. You don't need Nginx Plus to manage your cache smartly. A little command-line ingenuity is all it takes. Just be sure to use this power wisely – and maybe don't tell Nginx that their Plus version isn't always necessary for such tasks. &#128521;&lt;/p&gt;

&lt;h2&gt;Disclaimer&lt;/h2&gt;

&lt;p&gt;Look for &lt;code&gt;proxy_cache_key&lt;/code&gt; in your config files, becasue the keys might be generated differently in your setup. Also be cautious when using commands like &lt;code&gt;rm&lt;/code&gt; and always ensure you're targeting the right files. It's better to be safe than sorry, especially when dealing with server configurations.&lt;/p&gt;</description>
				<pubdate>Sun, 31 Dec 2023 00:15:42 GMT</pubdate>
			</item>
			<item>
				<link>https://www.jitbit.com/alexblog/314-the-state-of-modern-front-end/</link>
				<guid>https://www.jitbit.com/alexblog/314-the-state-of-modern-front-end/</guid>
				<title>The state of modern Front End</title>
				<description>&lt;p&gt;Let's talk about front-end. There'll be a lot of swearing, I'm sorry.&lt;/p&gt;

&lt;p&gt;About once every six months another blogger bursts into HackerNews/Twitter trends, saying - hey, enough of that JavaScript bloat, let's all use modern HTML controls!&lt;/p&gt;&lt;!--more--&gt;

&lt;p&gt;There's &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; for modal dialogs, there's &lt;code&gt;&amp;lt;input type=datetime-local&amp;gt;&lt;/code&gt; for date and time pickers, &lt;code&gt;&amp;lt;input type=color&amp;gt;&lt;/code&gt; for colors, and for progress bars there's &lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Last week another passionate young man has made a &lt;a href="https://catskull.net/html.html" target="_blank" rel="nofollow"&gt;romantic statement&lt;/a&gt;, scored almost 1000 upvotes on HN and rode into the sunset.&lt;/p&gt;

&lt;p&gt;And you know what, I agree. Makes me a believer every time, so I rush into rewriting everything. Happened again this last week.&lt;/p&gt;

&lt;p&gt;I mean, honestly. It's 2023 out there. Why is there no damn "select date/time" button in browsers? Why am I dragging a 100-kilobyte JS datetime picker dependency everywhere? I'm done. Enough! Let's rewrite everything to native datepickers. It's fast, lighweight, works natively on smartphones and supports dark theme out of the box.&lt;/p&gt;

&lt;p&gt;Aaaah-nd here comes... First, it turns out that &lt;code&gt;&amp;lt;input type=date&amp;gt;&lt;/code&gt; does not support &lt;code&gt;placeholder&lt;/code&gt;. That'd be the first JS workaround.&lt;/p&gt;

&lt;p&gt;Then it turns out you can not automatically show the dropdown on focus. More hacks. And no, "onfocus" won't work, hahah. "Onclick" is unreliable too.&lt;/p&gt;

&lt;p&gt;Some guy at Stackoverflow suggests making the dropdown icon transparent (!) and stretching it to the entire container. So, like, wherever you click - you hit that invisible icon. And everyone's like "oh, fuck... well, let's try."&lt;/p&gt;

&lt;p&gt;Then it turns out that the datepicker has weird &lt;code&gt;box-sizing&lt;/code&gt; and you have to hardcode pixels in CSS, otherwise it sticks out and looks different from all the other inputs.&lt;/p&gt;

&lt;p&gt;Oh, and it's also buggy in both Edge and even Chrome 114. Try giving it a date with seconds, then change the seconds in the UI and... it won't let you submit the form for some reason! And the validation error conviniently pops up UNDERNEATH the freaking dropdown. Come on, it's not even funny anymore&lt;/p&gt;

&lt;p&gt;&lt;img style="margin:auto" src="https://www.jitbit.com/uploads/1a4bKF9.png"&gt;&lt;/p&gt;

&lt;p&gt;...And in the end, mobile Safari will definitely break. Because it always breaks in the end.&lt;/p&gt;

&lt;p&gt;Native inputs my ass.&lt;/p&gt;

&lt;p&gt;And the icing on the cake: it turns out that Firefox... just doesn't support date-time picker at all. Ahaha. &lt;s&gt;Because fuck you, that's why&lt;/s&gt;. "Can-i-use" says it should work, "MDN" says it should work, but - nope... It just falls back to date-picker, there's no time picker at all, nada, not there. And "onchange" doesn't fire properly. And if a user selects the date only, and leaves the time empty (of course he fucking does, because there is no damn time picker!!!!11), then the entire input returns NULL. So what if you've picked the date - the whole damn thing gives you NULL in the POST-data too. Deal with it. And there's a bug at Firefox issue tracker unfixed for three years now, but hey, they'd rather work on fucking "Firefox Send", it's obviosly more important.&lt;/p&gt;

&lt;p&gt;I swear a couple more hours and I'm rolling it all back.&lt;/p&gt;

&lt;p&gt;Meanwhile clients are sending us support tickets "uhm, hi, your new date widget is kinda... weird..." Yeah, well, it's not fûcking ours &#120169;ÿya E ᵒh fuckingi no NO NOO̼O​O NΘ stop the an​*̶͑̾̾​̅ͫ͏̙̤g͇̫͛͆̾ͫ̑͆l͖͉̗̩̍ͫͥͨ ̟e ̠̅s ͎a̧͈͖r̽̾̈́͒͑e n​ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ TO͇̹̺ͅƝ̴ȳ TH̘Ë͖̉ ͠P̯͍̭O̚​N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝Sͯ ̨̥̫͎̭̔̀ͅ&lt;/p&gt;

&lt;p&gt;(send help)&lt;/p&gt;</description>
				<pubdate>Sat, 12 Aug 2023 10:21:38 GMT</pubdate>
			</item>
	</channel>
</rss>