AI 资讯
HTTP Caching Explained: max-age, ETag and Why Your Users Still See Last Week's CSS
📺 Prefer to watch? 90-second YouTube Short · 💬 Telegram Originally published on software-engineer-blog.com . You fixed the CSS. You deployed. You opened the site and checked it yourself — perfect. Then a customer sends a screenshot of last week's layout. Nothing is broken. No deploy failed, no CDN is lying to you, no file is corrupt. The browser is doing exactly what you told it to do, several days ago, in a header you probably never wrote by hand. This is the part of web performance that gets skipped, because caching looks like a setting rather than a contract. It is a contract. And like any contract, the interesting part is not what it gives you — it is what you can no longer do once you have signed it. Throughout this post I will use one running example: PlantPal , a small plant shop. One stylesheet ( app.css ), one logo ( logo.png ), one API endpoint ( /api/products ). The floor: a page load is not one thing Before caching means anything, you have to see what it is acting on. Loading PlantPal's homepage is not a request. It is roughly 40 separate requests — the HTML, the stylesheet, a few fonts, the logo, a dozen product images, the JavaScript bundle, the product API. Each one is a full round trip: DNS is probably warm, but you still pay connection setup, the request, the server's think time, and the bytes coming back. The numbers for a first visit: ~40 requests 1.2 MB transferred 2.1 s to a usable page Which gives us the only sentence in this post that you actually need to remember: The fastest request is the one the browser never sends. Not a faster server. Not a closer edge node. Not a smaller file. No request at all. Everything below is a way of getting closer to that. max-age: buying silence The blunt instrument is Cache-Control : HTTP / 1.1 200 OK Content-Type : text/css Cache-Control : max-age=31536000 31536000 is one year in seconds. You are telling every browser that receives this response: keep this copy and use it for a year without asking me again. O
AI 资讯
Switch Icons v0.2.0: A React Icon Library Built for the Icons Developers Actually Need
Modern web applications rarely need only arrows, menus, and generic interface icons. A fintech dashboard needs payment and banking icons. A logistics platform needs waybills, packages, warehouses, and delivery trucks. An AI application needs model, prompt, and AI-related visual language. An African commerce platform may need icons that represent local payment methods such as Naira, USSD, POS, and bank transfers. That is the idea behind Switch Icons. Switch Icons is a modern, developer-focused React icon library designed around practical icons for real-world applications—not simply another collection of unrelated SVGs. Why Switch Icons? There are already plenty of excellent icon libraries available. But while building modern applications, there is often a gap between the generic icons most libraries provide and the domain-specific icons developers actually need. Switch Icons is being built around that gap. Instead of focusing exclusively on generic UI elements, the library combines familiar interface icons with categories such as: Fintech and payment rails Logistics AI Commerce Technology Security Social Business and CRM Communication Media The goal is simple: make it easier for developers to find the right icon without having to create or hunt down an SVG every time they build a feature. What's New in v0.2.0? Switch Icons has now reached its first public npm release. Version 0.2.0 includes 93 icons across 9 major categories, along with 14 solid variants for icons where a filled visual style makes more sense. The current collection includes: Navigation & UI Essential icons for navigation, actions, and common interface patterns. People & Communication Icons for users, teams, messaging, communication, and related functionality. Business & CRM Icons designed for business applications and customer-management interfaces. Fintech & Payment Rails This is one of the areas that makes Switch Icons particularly different. The library currently includes icons such as: Naira Bank
AI 资讯
Fix Next.js "params should be awaited" Error in Next.js 15+
Fix Next.js "params should be awaited" Error in Next.js 15+ If you are seeing the params should be awaited Next.js error after upgrading to Next.js 15 or following an older App Router tutorial, you are not alone. The error usually looks something like this: Route "/blog/[slug]" used params.slug. params should be awaited before using its properties. Sometimes it appears with searchParams . Sometimes it appears with cookies() or headers() . And sometimes the page still seems to work, but your terminal keeps shouting at you. This article will slow it down and explain the fix in a beginner-friendly way. No deep framework lecture first. Just the actual problem, the broken code, the fixed code, and the reason it works. What This Error Means in Plain English In older Next.js code, you may have treated params like a normal JavaScript object. Something like this: const slug = params . slug ; That used to feel natural. If your route was: /blog/[slug] and the user opened: /blog/my-first-post you expected: params . slug ; // "my-first-post" In newer Next.js versions, especially Next.js 15+, some request-based values became asynchronous. That means you should treat them like values that need to be waited for before you read from them. So instead of reading params.slug directly, you do this: const { slug } = await params ; That is the heart of the fix. The error is not saying your route is missing. It is not saying your [slug] folder is wrong. It is saying: You are trying to read route data before awaiting it. The common flow: the page loads, the code reads params.slug directly, Next.js expects params to be awaited, and the error appears. Why This Changed Next.js has a group of features called Dynamic APIs . That sounds more complicated than it is. In simple terms, Dynamic APIs are values that depend on the current request. For example: What route did the user open? What query string is in the URL? What cookies came with this request? What headers came with this request? Is draft
AI 资讯
How I Built a Color Picker That Actually Converts Colors Correctly (HEX/RGB/HSL)
While working on a design system recently, I kept running into the same frustrating problem: I'd grab a color from Figma in HEX format, need it in HSL for a CSS variable, and end up bouncing between three different websites just to convert one value. Each site had its own UI quirks, some required JavaScript to be enabled, and none of them gave me a proper color scheme alongside the conversion. So I did what any reasonable developer would do — I built my own. Because apparently I enjoy reinventing wheels. The Problem With Existing Solutions The existing color converter tools online weren't bad, but they had a few issues that bugged me: They were slow — many loaded heavy JavaScript libraries just to do simple math They lacked context — I wanted to see complementary colors and schemes alongside the conversion They were ad-heavy — I don't want to dodge pop-ups while trying to match a shade of blue I wanted something that felt like a native tool: instant, offline-capable, and comprehensive. A single HTML file that I could open, use, and close without ceremony. The Architecture Decision The first decision was whether to use a library or write the conversion logic myself. Libraries like color (npm) are battle-tested, but they add weight. Since this is a browser-only tool with no build step, I decided to write the conversions in vanilla JavaScript. Here's the core conversion logic that handles the heavy lifting: function hslToRgb ( h , s , l ) { s /= 100 ; l /= 100 ; const k = n => ( n + h / 30 ) % 12 ; const a = s * Math . min ( l , 1 - l ); const f = n => l - a * Math . max ( - 1 , Math . min ( k ( n ) - 3 , Math . min ( 9 - k ( n ), 1 ))); return [ Math . round ( f ( 0 ) * 255 ), Math . round ( f ( 8 ) * 255 ), Math . round ( f ( 4 ) * 255 )]; } This is the most concise HSL-to-RGB conversion I know. It's a compact version of the standard formula that avoids the typical case-based approach. The math checks out for all edge cases, including grayscale (when s = 0 ). AI-Assi
AI 资讯
Perry Mason in: The Case of the Drifting Timer
Perry Mason in: The Case of the Drifting Timer Opening Statement You need a reactive "current time" in your Vue 3 app. A schedule grid with a red line showing "now." A live clock. A dashboard that updates every minute. Every Vue developer reaches for setInterval first. It works. But "works" and "works well" are different things. This is the story of taking a naive timer from "it ticks" to production-grade — and the four iterations it took to get there. The prosecution calls four exhibits. Let's begin. Exhibit A: The Memory Leak const currentTime = ref ( new Date ()) onMounted (() => { setInterval (() => { currentTime . value = new Date () }, 60000 ) }) It works. Sort of. The defense rests — but the prosecution is just getting started. Exhibits of negligence: The interval is never cleared. When the component unmounts, the timer keeps firing every 60 seconds forever — updating a ref nothing reads anymore, and holding its closure (and everything the ref references) in memory for the lifetime of the page. Silent. Invisible. The kind of leak that shows up in production after a user navigates around your app for 20 minutes. Exhibit B: Component-Only Cleanup const currentTime = ref ( new Date ()) let timeInterval = null onMounted (() => { currentTime . value = new Date () timeInterval = setInterval (() => { currentTime . value = new Date () }, 60000 ) }) onUnmounted (() => { if ( timeInterval ) clearInterval ( timeInterval ) }) Now we clean up. The interval is stored in a variable, cleared on unmount. A step forward. But onUnmounted has a scope limitation worth understanding: The limitation: onUnmounted only works inside components. If someone calls this logic from a Pinia store or outside a component's setup() context, onUnmounted never fires. The timer leaks silently. (Composables called synchronously during setup() are fine — Vue's docs recommend exactly that. The problem is when there's no component instance at all.) The timer fires 60 seconds after load , not at the t
AI 资讯
Three of the First Four Alerts Were the Question's Fault
Last week I turned my data audit into a build step : a check that runs before anything else and fails the build when the database and any static copy of my travel site's legal-status data disagree. It ended the era of the site contradicting itself. It did nothing about the site agreeing with itself on something false. That's not a hypothetical. The most expensive error the whole project found was a country whose law changed in January while every copy on my site — database, data files, search index — kept saying the old thing in perfect unison. Internal consistency was the camouflage . No diff between my own sources could ever have caught it, because every internal source was equally behind the world. A build gate proves agreement. Agreement is not truth. Something has to look outside. You can't diff against the world, but you can sample it The naive version of "look outside" is another audit — a human session checking primary sources jurisdiction by jurisdiction. I've done three of those now, and I know exactly what they're worth: they're correct the day they ship and they decay from that morning on. Laws don't change on my audit schedule. So the outside check became what the inside check became: a scheduled job. Once a week, a script asks a web-connected model — one that searches and cites, not one answering from training memory — for the current legal status of about fourteen jurisdictions, and compares each answer to the corresponding database row. Fourteen, not all 271, because the selection is doing the real work: A hot list is checked every single run: the highest-traffic pages plus the jurisdictions with active legislative motion — the places where being a month stale costs the most. Everything else sits on a rotating cursor : eight per run, round-robin, so every row on the site gets sampled roughly twice a year without any run costing more than a few cents. The whole thing runs on about seven cents a week. Two rules were non-negotiable, both inherited from
开发者
A Pet Project: Zodiacl
It’s been awhile since I typed some words around here. Right around the World Cup was the last time, from what I can see. I really didn’t want to like the World Cup, but there’s something about watching it I family that makes it about more than just a game. But I digress - this post has nothing to do with football (fortunately). This is about a pet project I’ve been kicking around for some time, and finally got around to fleshing it out and making it live. A Chinese and Western Zodiac calculator. I’m not a hard-core zodiac horoscope person. I just find the topic interesting. From time to time people would say, “Hey what sign am I?”. I’d spin up the ol’ internet and look for a good Chinese Zodiac calculator (Western Zodiac is much simpler to calculate). But the calculators were usually either deeply embedded in larger sites that were not dedicated to the topic, or ugly as hell and ridden with ads (and who knows what else). I really wished to just find a nice-to-look-at calculator that worked. And one day I realized - hey, I could just make one. I make nice looking websites. I can code enough to set up a Chinese Zodiac calculator that can accurately calculate lunar cycles and your Chinese zodiac animal, without blindly saying, “Oh, you were born in 2026? You’re a horse, of course!” (Which would only be true if you were born on February 17th of 2026 or later). You know, a simple tool that does one thing, and one thing well - show you your sign! (So you can stop asking people in bars…) Zodiacl is Born If you want to know more about the technical side of the new site, you can check it out in my portfolio. So I was probably about halfway through coding the project when the name dawned on me. Unless a client or project needs otherwise, I code and publish for an English- and Spanish-speaking audience. I deeply favor names that work in both languages; the closer to a perfect cognate (word spelled exactly the same in both languages and with the same meaning) the better. I fou
开发者
I Built 132 Free Online Tools Because I Kept Searching for Them
As a developer, I constantly end up searching for small tools to do random things. Format JSON, decode JWTs, generate UUIDs, encode URLs, compare text, convert data, and so on. I got tired of opening a different website every time, so I started building my own collection. That's CtrlTool. It currently has 132 free tools for developers and everyday tasks, with a focus on keeping them fast, simple, and easy to use. A lot of the tools process data directly in the browser when possible. https://ctrltool.wtf It's still very new, so I'd love to hear what tools you think are missing.
AI 资讯
React State Management in 2026 — Context API vs Redux Toolkit vs Zustand vs Jotai (Same Cart, Real Code + Benchmarks)
The React state-management debate has produced more bad takes than any other frontend topic. "Just use Context." "Redux is dead." "Zustand for everything." "Jotai is the future." All four are partially right and partially dangerous, depending on what you're building. So instead of arguing, I built the same shopping cart — derived totals, async fetch, localStorage persistence, three subscribing components — in all four libraries , and benchmarked it. This is the condensed version; the full guide (all four implementations with real code, the complete matrix, and the decision flow) is on my site 👇 Full guide: https://prepstack.co.in/blog/react-state-management-context-redux-toolkit-zustand-jotai-comparison-guide The one benchmark that reframes everything 1,000 components subscribed to one store. Update one value. How many re-render? Library Components re-rendered Wall-clock Context (single value) 1,000 (all) 42 ms Context (split into 5) ~200 12 ms Redux Toolkit (selectors) 1 2.1 ms Zustand (selector) 1 1.8 ms Jotai (atom) 1 1.5 ms Context without splitting re-renders the world. The other three are within margin of each other — meaning the real differences are boilerplate and DX , not render speed. The four, in one line each Context API — built-in, 0 KB, but every consumer re-renders on any change. Right for theme/auth/locale; wrong for anything busy or with many subscribers. Redux Toolkit — ~22 KB, most boilerplate, but RTK Query (caching, dedupe, invalidation), middleware, and time-travel DevTools are best-in-class. Payoff scales with app complexity. Zustand — ~3 KB, no provider, selectors built in, a full store (state + async + persistence) in ~25 lines. The modern default for most 2026 apps. Jotai — state is many small atoms, each with its own subscriber list. Smallest blast radius per update; ideal for forms and derived graphs. Real production migration (same e-commerce app) Metric Context-everywhere Redux Toolkit Zustand Initial JS (gzipped) 412 KB 438 KB 390 KB A
AI 资讯
Foodwars: Battle of the Comfort Foods
What if deciding what to eat felt as exciting as winning a championship? It's 2 AM. You're hungry. You open your favorite food delivery app, convinced you'll order something in two minutes. Thirty minutes later, you're still scrolling. Pizza? Burger? Pasta? Fries? Momos? Ice cream? Suddenly, every option looks equally good, and now you're questioning your entire existence just because you wanted dinner. I have this problem almost every time I order food. So when I saw the DEV Challenge, I wanted to build something fun around this tiny but painfully relatable problem. Unfortunately, I couldn't finish it before the deadline, but I still wanted to share the idea because it's one of those projects that made me smile while building it. Meet Foodwars . Instead of endlessly scrolling through hundreds of dishes, why not let your favorite comfort foods battle each other until only one champion remains? What I Built We've all watched cooking shows like MasterChef and somehow turned into professional judges sitting comfortably on our sofas. "That steak is overcooked." "The sauce needed more balance." "I would've plated it differently." As if Gordon Ramsay personally asked for our opinion. Foodwars lets us finally put those imaginary judging skills to good use. Instead of comparing hundreds of dishes at once, the platform randomly pairs comfort foods against each other in head-to-head battles. You become the judge. Pick the winner, move on to the next matchup, and continue until one food survives the tournament. No endless scrolling. No decision fatigue. Just a series of fun, quick decisions that eventually crown your Ultimate Comfort Food . And once the champion is decided... Go order it. Or cook it. Either way, dinner has finally been decided. Demo comfort-foodwars.vercel.app Features of Foodwars Foodwars isn't just a random food picker. Every round is designed to make choosing food feel like a game instead of a chore. 1. Interactive Tournament Brackets Instead of presenting
AI 资讯
Single-database multi-tenancy in Symfony: a 31-line Doctrine filter, and the five places it never runs
Single-database multi-tenancy is the cheapest kind: one schema, one connection, an organization_id column on every tenant-owned table. The whole design rests on one promise, and it is a promise about forgetting : no developer on the team will ever have to remember to write WHERE organization_id = ? , because forgetting it once leaks another customer's data. Doctrine has had the tool for this for years. It is a SQLFilter , it is about thirty lines, and almost every article about it stops at the happy path. The interesting part is not the filter. It is the map of the places where it is simply not there, because that map is what you actually have to defend. Everything below is read from Doctrine ORM 3.6.7 and from a suite that runs on every commit. The filter final class OrganizationFilter extends SQLFilter { public const string NAME = 'organization' ; public const string PARAMETER = 'organization_id' ; public function addFilterConstraint ( ClassMetadata $targetEntity , string $targetTableAlias ): string { if ( ! $targetEntity -> getReflectionClass () -> implementsInterface ( OrganizationOwnedInterface :: class )) { return '' ; } return \sprintf ( '%s.organization_id = %s' , $targetTableAlias , $this -> getParameter ( self :: PARAMETER )); } } OrganizationOwnedInterface is a marker with one method, getOrganization() . An entity opts into tenancy by implementing it, and that is the entire public API of the mechanism. No attribute to remember, no base class to extend, no trait whose absence is invisible in a diff. The filter is declared in doctrine.yaml with enabled: false . That is deliberate, and it is the first design decision worth arguing about: a filter that is on by default in the container is on in your fixtures, in your migrations, in your data-repair scripts, and it will bite you at three in the morning. It gets turned on by the layer that knows who is asking. The layer that knows who is asking public static function getSubscribedEvents (): array { // Right aft
产品设计
S3 Egress Fees: Why Downloading Your Own Data Costs So Much
Cross-posted from the Runsite blog . You put a few hundred gigabytes of images on object storage, glance at the pricing page, and the numbers look friendly: storage is a couple of dollars a month, basically a rounding error. Then the first real invoice arrives and it's a hundred and something. Nothing about how much you're storing changed. The line that blew up isn't storage at all. It's egress — the charge for data leaving the bucket — and it's the part of the bill nobody shops on. Why the storage bill blows up after the first invoice The pricing page wasn't lying to you. Object storage genuinely is cheap to sit on. On AWS S3 , standard storage runs about $0.023 per GB per month at the time of writing, so a hundred gigabytes of assets costs you around two dollars and change to keep. That's the number you compare when you're choosing where to put your files. The number you don't compare is egress: the fee for moving data out of the provider's network. It doesn't show up when you upload, and it doesn't show up while the files just sit there. It shows up every time someone downloads something — roughly $0.09 per GB to the internet once you're past a small free allowance (about the first 100 GB a month on AWS). Individually those are tiny fractions of a cent. The trouble is you're not billed once. You're billed per download, and a popular file gets downloaded a lot. Where egress hides Egress is data transfer out: every byte that leaves the provider's network. The reason it surprises people is that it isn't a single line you can point at. It's a multiplier that quietly attaches itself to things you'd never think of as "downloading": Serving assets to users. Every image, video, PDF, or download your app hands to a visitor is egress. One 4 MB hero image on a page that gets a million views a month is four terabytes of transfer out, from a single file. CDN origin pulls. Putting a CDN in front of your bucket helps, but it isn't free. Every cache miss means the CDN fetches th
AI 资讯
I Built a Claude Code Skill That Reverse-Engineers Undocumented APIs
I Built a Claude Code Skill That Reverse-Engineers Undocumented APIs Because "the docs are in the code" is not a documentation strategy. The Week I Lost to Grepping I joined a new team last month. Day 1 task: add a feature to the billing service. Day 1 reality: I opened the API docs and realized they were from 2022. Half the routes had been rewritten. The other half never had docs to begin with. So I did what every backend dev does. I grepped. grep -r "app.get|app.post|router." src/ --include="*.js" Four hours later, I had a notebook full of endpoints, a headache, and zero confidence that I had found everything. I found routes that worked but were not documented. I found docs for routes that did not exist anymore. I found one GET /invoices/:id endpoint with zero auth checks that had been sitting there since 2022. This is normal. And it should not be. The Idea What if I could drop a single file into a repo and have Claude Code map the entire API layer for me? Not from annotations. Not from existing OpenAPI specs. From the actual code. So I built it. Meet API Archaeologist API Archaeologist is a Claude Code / Codex CLI skill that reads your source code and reverse-engineers your API layer. It finds: • Internal endpoints — REST, GraphQL, gRPC, WebSockets • External integrations — third-party APIs, webhooks, SDK clients • Auth flows — JWT, OAuth, API keys, session cookies, RBAC • Security gaps — unauthenticated routes, hardcoded secrets, missing rate limits • Dead code — auth middleware with no endpoints, orphaned routes And it generates two things: API_DISCOVERY.md — A complete catalog with Mermaid diagrams openapi-draft.yaml — A draft OpenAPI spec How It Works The skill is just a SKILL.md file. Claude Code reads it and follows the instructions. It: Discovers route definitions Traces handlers, DTOs, middleware, services, and database calls Maps authentication and authorization Finds external API calls and integrations Flags potential security and reliability risks Gene
AI 资讯
AI Incident Copilot Guide for GCC Operations
🚀 Technical Briefing: This tutorial is part of our deep-dive series on Agentic Workflows at Gate of AI . For the full technical breakdown, interactive code sandbox, and the native Arabic translation, visit the original article here . <p>Tutorial</p> <h1>Design a Safer AI Incident Copilot for GCC Operations</h1> <p>An AI incident copilot can help an operations team turn approved engineering facts into a clearer draft for stakeholders. It should not be treated as an autonomous incident commander, a source of truth, or an automatic publishing system. This tutorial explains how to define a safe operating model before choosing a framework, model provider, deployment platform, or integration.</p> <h2>Why incident copilots need a security-first design</h2> <p>During an incident, teams work under pressure. They need to communicate what is happening, who may be affected, what mitigation is under way, and when the next update will arrive. These messages must be accurate, calm, and consistent. An AI assistant may help prepare a first draft, but it can also amplify mistakes if it is allowed to infer missing facts, read untrusted material, or publish messages without review.</p> <p>The available security research on Copilot-style systems is a direct reason to design cautiously. Researchers have demonstrated ways AI systems can be manipulated to provide false references to files, extract some private data, and bypass security protections. The same research describes proof-of-concept abuse that can turn an AI assistant into an automated spear-phishing mechanism after an attacker gains the necessary access. These are not minor quality issues. They show that an AI feature connected to organizational information can become a security boundary.</p> <p>For an incident copilot, the safest initial scope is deliberately narrow: accept a small set of verified facts supplied by an authorized incident lead, create a draft in a fixed communication format, and require a human to review and pub
AI 资讯
Sandboxed Code Evaluation for AI-Generated Outputs — How I Built SafeCode Arena
The Problem: Candidate Code Without Trust You're using Cursor, Claude Code, or GitHub Copilot. The AI gives you three implementation options for the same feature. AI: "Here are three approaches: A) Quick but uses unsafe B) Slower but memory-safe C) Balanced tradeoffs" You: "Which one should I ship?" AI: "It depends..." That "it depends" is where responsibility falls through the cracks. Tests tell you if code compiles and passes specs. But they don't tell you about security, performance, maintainability, or resource limits — all at once. You end up making the call by gut feel. This essay is about building a system that doesn't let that happen. The Solution: Multi-Axis Scoring I built SafeCode Arena — an automated verifier that evaluates code candidates across five axes simultaneously, scores each, and surfaces the tradeoffs. The Five Axes Axis Weight Computation Correctness 50% compile (40%) + tests (40%) + property tests (20%) Security 20% unsafe heuristics (50%) + clippy warnings (50%) Performance 15% relative compile+test time across candidates Maintainability 10% function-length heuristics (60%) + clippy (40%) Resource Usage 5% pass/fail of sandboxed Wasm execution Why These Five? Correctness dominates — code that doesn't work is valueless, so it's 50% Security is explicit — unsafe compiles fine, but you need to detect it yourself Performance and maintainability matter equally — a fast mess vs. a slow masterpiece aren't comparable Resource limits are real — a 100-point algorithm that consumes 2GB is a fail in production Example Scorecard Candidate A: 85 points ├─ correctness: 100 (all tests pass) ├─ security: 60 (2 unsafe blocks flagged) ├─ performance: 70 (10% slower than B) ├─ maintainability: 85 (avg function 25 lines) └─ resource_usage: 80 (Wasm sandbox: 512MB, OK) Candidate B: 92 points ✓ Recommended ├─ correctness: 95 (1 edge case warning) ├─ security: 95 (no unsafe) ├─ performance: 95 (fastest) ├─ maintainability: 88 (avg function 20 lines) └─ resource_usa
开发者
State Management in Front-end Web Development: Mutators
Libraries like Valtio and Pinia for Vue use a mutator pattern instead of the actions, dispatch, and...
开源项目
CareCloud confirms 3.7M patients had their medical records stolen in data breach
The cyberattack at CareCloud resulted in one of the largest reported data breaches in the U.S. healthcare industry this year.
AI 资讯
A practical guide to live streaming protocols, latency and scaling
Live video looks simple until you build it. Then you discover that "low latency" means five different things, that your CDN and your latency target are fighting each other, and that the box which handled ten viewers falls over at ten thousand for reasons nobody warned you about. This is the guide I wish existed when I started. No vendor talk, just how the pieces fit. 1. Ingest and delivery are separate decisions The single most common mistake is treating "streaming protocol" as one choice. It is two. Ingest is getting video from a camera, encoder or browser into your server. Delivery is getting it from your server to viewers. They have different constraints and you almost never use the same protocol for both. A typical stack ingests over RTMP or SRT and delivers over HLS. Another ingests WebRTC and delivers WebRTC. Mixing is normal and expected. Once you separate them, most of the confusion disappears. 2. The ingest protocols RTMP is old, TCP-based, and still everywhere. Every encoder speaks it, OBS defaults to it, and it just works. Latency is typically 2 to 5 seconds. Classic RTMP is limited to H.264 and AAC, though the Enhanced RTMP spec has added HEVC and AV1. Being TCP, it degrades badly on lossy networks: packet loss becomes head-of-line blocking, and your stream stalls instead of gracefully dropping quality. SRT is the answer to that. UDP-based with its own retransmission layer (ARQ), a configurable latency buffer, and built-in AES encryption. It is designed for pushing broadcast-quality video across the public internet, which is exactly where RTMP struggles. If your source is on a flaky connection, a 4G link, or a different continent, SRT is usually the right call. # Publishing over SRT with ffmpeg ffmpeg -re -i input.mp4 -c copy -f mpegts \ "srt://your-server:4200?streamid=live/stream1" RTSP is what IP cameras speak. If you are pulling from surveillance hardware, you are pulling RTSP whether you like it or not. WHIP (WebRTC-HTTP Ingestion Protocol) is the n
AI 资讯
Namecheap closes every auction at 11:00 AM ET. Last-second bidding is a myth.
If you have ever tried to win a domain at auction, you probably assumed the game works like eBay: watch the clock, wait for the last eight seconds, fire your bid, walk away with the name. On Namecheap, that does not work. Not "works badly". Does not work. Namecheap's expiring and marketplace auctions close in a daily batch at 11:00 AM ET. Every auction ending that day ends at roughly the same moment, which means there is no quiet corner of the day where you and one other bidder are paying attention. And if a bid lands in the closing window, the auction extends. So the buzzer-beater you were planning gets absorbed and the clock keeps running. The winner is not the fastest click. The winner is whoever set the smartest proxy maximum, on a name they found before anyone else was looking at it. I have been building PounceDomains around that one fact for months, and it is the reason the product looks the way it does. The edge moved from timing to discovery If speed is not the lever, the levers left are: find the good names earlier, and know what they are actually worth before you commit a number. So the engine scans the Namecheap aftermarket around the clock rather than at the bell. You describe the domains you want in plain English, something like "pronounceable 5-letter .com brandables under $50, no numbers or hyphens", and it builds a tuned config you can edit. If your config is too broad, it tells you and tightens it. There are seven scoring lenses you can stack: pronounceable, brandable, exact-match keyword, short premium, dictionary word, two-word combo, and free-text custom criteria. Fast programmatic filters run first, then AI scores what survives, and only domains that clear your threshold become matches. It has graded over 340,000 domains so far. The second lever is the one I care about more. Every match arrives with its receipts The failure mode in domain investing is not missing a name. It is paying $400 for something worth $80 because a free appraisal tool pri
AI 资讯
GitHub API Rate Limits: an Unauthenticated 304 Still Costs You a Request
No token. One IP. July 29, 2026: GET /repos/python/cpython 200 5996 B remaining 32 -> 31 + If-None-Match (no Authorization header) 304 0 B remaining 31 -> 30 + If-None-Match 304 0 B remaining 30 -> 29 + If-None-Match 304 0 B remaining 29 -> 28 Three conditional requests. Three 304 Not Modified . Zero bytes of body across all three. Three requests gone from a bucket of 60 per hour. I opened the terminal to write the opposite post. The short version: if you call the GitHub REST API without an Authorization header, an If-None-Match request that comes back 304 still decrements x-ratelimit-remaining . The ETag saves you bytes. It does not save you quota. GitHub's documentation states the claim five times on one page and attaches the condition to two of them, and that clause falls off easily when a sentence gets quoted on its own. The post I meant to write My working title was something like "poll GitHub for free with ETags". I believed it. I had read the sentence about 304 responses not using your rate limit, I had repeated it to other people, and the plan was a tidy little piece with a before-and-after budget chart. The first run killed it. remaining went down. My first reaction was that my counter reading was wrong, which is the normal reaction and usually the correct one. It was not wrong. So the post changed, and the finding turned out to be worth more than the one I went in with. Does a 304 count against the GitHub rate limit? What the docs actually say Here is the part that matters, and I want to be precise because it would be easy and dishonest to turn this into "GitHub's docs are wrong". They are not. On the page Best practices for using the REST API the claim shows up five times. Two of the five carry a condition; three do not. Here is the strict one, the only place on the page where the condition is spelled out as a header: "Making a conditional request does not count against your primary rate limit if a 304 response is returned and the request was made while c