AI 资讯
New Here !! Am i Late?
Hey everyone! I’m Musab, a developer and someone who’s recently started getting more involved in the developer community. I’m interested in building real-world products, exploring AI, full-stack development, and contributing to open-source projects. Over the past few months, I’ve been working on 30+ projects and have started contributing to open source, including having PRs merged into projects like AI-System-Design-Consultant, OpenLife, and OpenSRE. Tech I work with: React • TypeScript • Tailwind CSS • Node.js • Python • Flask • MongoDB • MySQL • Redis • Firebase • AWS • Docker I’m here to learn, build, contribute, and connect with other developers. Looking forward to meeting everyone and being part of the community. DeveloperCommunity #OpenSource #FullStackDevelopment #AI #WebDevelopment #SoftwareDevelopment
AI 资讯
Why Your Eyes Burn by Evening: Digital Eye Strain and the 20-20-20 Rule
By the end of the day my eyes burn. The screen goes fuzzy for a second when I look up, focusing on something across the room takes longer than it should, and a dull headache creeps in around the temples. I used to write this off as "just tired." Turns out it has a name and a fairly simple mechanism behind it. What computer vision syndrome actually is Computer vision syndrome — digital eye strain, if you prefer the plainer name — isn't a diagnosis in the sense of "something broke." It's a cluster of symptoms that shows up after prolonged close-range screen work: dryness and burning, blurred focus when you shift your gaze to something far away, light sensitivity, headaches, and often neck and shoulder pain, because we unconsciously lean toward the screen and freeze in one position for hours. Two things happen at once. First, your eyes hold focus on a near object for a long stretch — the ciliary muscle, which controls how the lens changes shape for near vision, stays tensed the whole time instead of periodically relaxing the way it would if your gaze wandered farther away now and then. Second, you blink noticeably less often while concentrating, so the tear film that keeps your eyes moist doesn't get replenished as frequently — hence the dryness. Why a screen and not a book Reading a book for hours also holds your focus at close range, but it strains your eyes less, and there's a reason for that. A screen emits light rather than reflecting it the way paper does, which creates more contrast against the room's ambient lighting, especially if the room is dimmer than the display. Glare from windows and lamps forces you to squint and refocus. And a laptop or phone tends to sit closer to your face than a book or a printed document would, simply because the screen is smaller. There's also the nature of the work itself. Reading a book is a steady stream; working with software is a constant series of micro-refocuses between windows, tabs, and notifications. Your eyes keep re-ad
AI 资讯
Node.js Express vs. Python FastAPI: Which Should You Choose in 2026?
Node.js Express vs. Python FastAPI: The Definitive Guide for Choosing Your Next Backend Choosing a backend framework used to be simple. If you liked JavaScript, you built with Express. If you liked Python, you went with Flask or Django. But the landscape has fundamentally shifted. With the explosion of AI, machine learning, and strict type safety, Python FastAPI has emerged as a powerhouse alternative to the traditional JavaScript runtime. Meanwhile, Node.js Express remains the unopinionated king of the enterprise web. If you are starting a new project today, which one should you choose? Let’s break down the technical trade-offs, developer experience, and code structures of both frameworks. 🚀 The Core Philosophy Node.js Express: The Minimalist Canvas Express is a minimalist, unopinionated framework. It doesn't care how you structure your folders, how you validate data, or how you handle errors. It gives you a robust set of HTTP tools and steps out of your way. The Catch: You have to build or install your own solutions for data validation, ORM mapping, and API documentation. Python FastAPI: The Automated Powerhouse FastAPI is built on modern Python 3.8+ features like type hints and asynchronous ASGI (asyncio). It is highly opinionated about data handling, leveraging Pydantic to automate input validation and schema serialization. The Catch: It forces you into a specific way of handling data types from day one, which can feel restrictive if you prefer absolute freedom. 📊 Feature Breakdown Feature Node.js Express Python FastAPI Language JavaScript / TypeScript Python Data Validation Manual / Third-Party (Zod, Joi) Native via Pydantic API Docs Manual Setup (Swagger UI plugin) Automatic (Interactive Swagger UI & ReDoc) Best For Real-time I/O, WebSockets, Full-stack JS AI/ML APIs, Data pipelines, Type-safe apps 🛠️ Code Comparison: Creating a Validated POST Route Let’s look at how both frameworks handle a common task: creating a POST endpoint that accepts an item, validates
AI 资讯
100 Writing, Productivity, Coding & Research Lenses for ChatGPT 🧠💻
From fixing one sentence to designing an algorithm, AI becomes much more useful when you stop treating it as a single-purpose chatbot. Instead, think of it as a collection of specialized working modes . Need to debug? /debug Need to design an algorithm? /algorithm Need to plan research? /researchplan Need to challenge your own argument? /critic Need to turn a large project into manageable work? /roadmap90 The underlying idea is simple: Don't just ask AI for an answer. Give it a mode of thinking. From Prompt → Workflow A normal interaction might look like: User ↓ Question ↓ AI ↓ Answer A structured workflow looks different: Goal ↓ Context ↓ Lens ↓ Analysis ↓ Output ↓ Review ↓ Iteration For example: Project ↓ /researchplan ↓ Research questions ↓ /hypothesis ↓ Testable assumptions ↓ /experiment ↓ Evaluation ↓ /audit ↓ Final findings The shortcut is not magic. It is a task-specific instruction layer . 1. Writing Lenses The first group focuses on transforming existing text. /rewrite /improve /polish /proofread /grammar /copyedit /expand /shorten /paraphrase /simplifytext These commands represent different operations. For example: /rewrite should preserve the original meaning while changing the wording. Whereas: /improve can address: clarity structure flow word choice readability And: /shorten optimizes for concision. This distinction matters because: Editing and rewriting are not the same task. 2. Tone Is a Control Variable The next group controls communication style: /formal /casual /friendly /professional /persuasive /convincing /academic /journalistic The same information can be communicated differently depending on the audience. For example: Technical explanation ↓ ┌──────┼──────┐ ↓ ↓ ↓ Student Developer Executive The underlying facts should remain stable. The presentation changes. That makes tone a communication parameter , not merely decoration. 3. Structured Writing For longer outputs: /story /essay /article /report /whitepaper /casestudy /proposal /sop /playbook
AI 资讯
Your Form Is Not Portable If It Contains Callbacks
What makes a form portable? Not JSON alone. Its validation, conditions, collections and submission semantics must survive the trip too. I wrote about the architecture behind Modyra and the trade-offs involved. Your Form Is Not Portable If It Contains Callbacks Most form libraries help us manage forms inside an application. They track values, execute validators, expose errors and eventually produce a submission payload. That works well until the form needs to exist somewhere else. Perhaps its structure comes from a backend. Perhaps a visual builder generates it. Perhaps multiple applications must render it. Perhaps the server must independently validate the same conditional rules used by the browser. At that point, the form is no longer just component state. It is a contract. And most form abstractions cannot cross that boundary. The portability illusion Consider a typical conditional validator: const form = createForm ({ defaultValues : { country : ' IT ' , vatId : '' , }, validators : { onChange : ({ value }) => { if ( value . country === ' IT ' && ! value . vatId ) { return { fields : { vatId : ' VAT ID is required in Italy ' , }, }; } }, }, }); This is perfectly reasonable application code. It is also not portable. The callback cannot travel through an API as JSON. A Java service cannot execute it. A visual editor cannot reliably inspect it. Another runtime cannot reproduce its meaning without receiving executable source code. We can serialize the values around the callback, but not the behavior itself. This leads to an important distinction: A form configuration is not a portable form contract if part of its meaning still lives inside executable callbacks. The obvious shortcuts are dangerous There are several tempting ways to work around this limitation. Serialize the callback as source code { "condition" : "value.country === 'IT'" } The receiving application must now parse or execute an expression encoded as text. That creates immediate problems: the expression
AI 资讯
Static Forms in Astro: Handling Submissions Without a Server
Static Forms in Astro: Handling Submissions Without a Server with onsubmit.dev (form backend) Astro is a great fit for content-heavy sites that ship very little JavaScript, but that creates an interesting problem as soon as you add a contact form: where does the POST request go? With onsubmit.dev (form backend) , an Astro site can submit forms to an external endpoint instead of adding its own API route or server. This is particularly useful for Astro projects deployed as static files to a CDN, GitHub Pages, or another static host. You can keep the site static while still accepting contact requests, feedback, registrations, and similar submissions. Start with the zero-JavaScript pattern The simplest approach is also the most aligned with Astro's philosophy: use the browser's native form submission behavior. You don't need a hydrated component merely to collect a few fields. A regular HTML form can make a POST request directly to a form backend: --- // src/pages/contact.astro --- <form method="POST" action="https://onsubmit.dev/f/YOUR_FORM_ID"> <label> Name <input type="text" name="name" required /> </label> <label> Email <input type="email" name="email" required /> </label> <label> Message <textarea name="message" required></textarea> </label> <button type="submit">Send message</button> </form> Replace YOUR_FORM_ID with the endpoint supplied for your form. There is no client framework involved here. The browser serializes the named fields and sends them directly when the visitor clicks the button. That has several nice properties for an Astro project: No Astro server endpoint is required. No React, Vue, or other client runtime needs to be hydrated. The form still works when JavaScript is unavailable. Your static deployment remains static. It is worth remembering that native HTML already does a lot of work. required , type="email" , labels, and standard browser submission cover many simple forms without additional JavaScript. Where astro-onsubmit fits For Astro-specif
AI 资讯
reCAPTCHA: It’s Not Just “I’m Not a Robot”
How CAPTCHA evolved from typing distorted text to analyzing behavior, context, and risk When most people hear CAPTCHA, they imagine a small checkbox: ☐ I’m not a robot Or perhaps a challenge asking them to select traffic lights, bicycles, buses, or crosswalks. But modern reCAPTCHA is much more interesting than that. In many cases, you don't actually solve anything. You simply open a webpage, move your mouse, click a button, fill out a form—and somewhere in the background, a risk-analysis system is trying to answer a much harder question: “Does this interaction look like a legitimate human interaction, or automated/abusive traffic?” That is a fundamentally different problem from asking a user to identify a picture. Google describes reCAPTCHA as a service that uses advanced risk-analysis techniques to distinguish humans from bots. Modern versions can return a risk score instead of presenting a visible challenge. 1. The original CAPTCHA problem CAPTCHA originally stood for: Completely Automated Public Turing test to tell Computers and Humans Apart. The basic idea was simple: Humans are good at recognizing distorted characters. Traditional computer programs were not. So the website could display something like: but distort, rotate, or obscure the characters. The user typed: 7hK9P and the website accepted the answer. This created a simple classification: It worked reasonably well. Until machines became better. 2. Then computers learned to read the CAPTCHA This created an interesting security race. CAPTCHA became harder. Then OCR and machine learning became better. So CAPTCHA became even harder. Eventually the system was moving toward: Human intelligence vs machine vision And that created an unfortunate side effect. The better the security became, the worse the experience became for legitimate users. Instead of: «“Are you human?”» the user was suddenly being asked: «“Select every square containing a traffic light.”» And sometimes: «“Select every square containing a traffi
AI 资讯
How GoFundMe became America’s backup plan
Today on Decoder, I’m talking with Tim Cadogan, the CEO of GoFundMe. You know GoFundMe — it’s a major fundraising platform where you can donate to help people with everything from medical expenses to little league uniforms to starting a new business. Tim took over as CEO in March 2020 — a fascinating and chaotic […]
开发者
JWT Authentication in Node.js: A Practical Guide (with Express)
Ever logged into an app, closed the tab, come back, and you're still logged in — no password needed? That's almost always JWT doing its job behind the scenes. JWT (JSON Web Token) is one of the most common ways to handle authentication in modern backends. But a lot of developers use it without really understanding what's happening — and that's exactly where security bugs sneak in. Let's fix that. By the end of this post you'll know what a JWT actually is, how to use it in a Node.js + Express app, and the mistakes that quietly break real apps. What is a JWT, really? A JWT is just a string with three parts , separated by dots: xxxxx.yyyyy.zzzzz │ │ │ header payload signature Header — says which algorithm signed the token (e.g. HS256 ). Payload — the actual data (like userId , role , and an expiry time). This is not encrypted — it's just Base64-encoded. Anyone can read it. Signature — a cryptographic stamp created using a secret only your server knows. This is what stops people from faking tokens. Want to see this for yourself? Paste any token into a free JWT decoder and you'll instantly see the header and payload. Notice you can read everything without the secret — that's the key lesson: never put passwords or sensitive data in a JWT payload. Creating a token (login) Install the library: npm install jsonwebtoken When a user logs in successfully, sign a token: import jwt from ' jsonwebtoken ' // On successful login: const token = jwt . sign ( { userId : user . _id , role : user . role }, // payload process . env . JWT_SECRET , // secret (keep it in .env!) { expiresIn : ' 7d ' } // auto-expiry ) res . json ({ token }) Three things to notice: Keep the payload small — just an id and role, not the whole user object. The secret lives in an environment variable, never hardcoded. Always set expiresIn . A token that never expires is a token that can be stolen forever. Verifying a token (protecting routes) Now create a middleware that checks the token on every protected request
AI 资讯
Your Developers Are Coding Faster. So Why Is Delivery Still Slow?
More and more developers are using AI assistants to write code. With these tools, teams can move from an idea to implementation much faster than before. Logically, overall delivery should speed up as well — but often not as much as we would expect. A team might reduce implementation time by 30% or even 50%, while the time it takes for a feature to actually reach production changes only slightly. The reason is that other stages of the workflow — waiting for code review, QA, testing, approvals, and release — do not automatically speed up just because coding does. That’s why it’s important to ask: if AI has significantly accelerated coding, why isn’t overall delivery time improving at the same pace? Coding time is only one part of delivery time Let’s imagine a typical workflow in a development team: To Do → Development → Code Review → Awaiting QA → Testing → Ready for Release → Done By breaking down the time in the status in more detail, you can see the following picture: 2 days in Development 2 days waiting for review 3 days waiting for QA 1 day in Testing 2 days waiting for release Development time : 2 days. Total delivery time : 10 days. With AI becoming part of the development process, it’s entirely possible to cut implementation time in half. In our example, that means cutting it from 2 days to 1. That's a 50% improvement in Development. But if everything else stays the same, the total delivery time drops from 10 days to 9, which is only a 10% improvement . The team really did get faster at writing code — the improvement just happened in one part of a much larger delivery system. Faster coding can expose the next constraint Think of the workflow as a sequence of stages, each with its own capacity. When Development becomes faster, more work can reach downstream stages sooner. If Code Review, QA, Testing, or Release have enough capacity to absorb that work, delivery improves. If they don't, some of the productivity gain turns into queue time. The delay hasn't necess
AI 资讯
One View Per Layer: Four Sharp Edges I Found in My Own Code
There is a layer in my database called 1 . Somebody created it, presumably by accident, and it sat there for months looking harmless. It was the only layer in the system that never served a single tile, and nobody noticed, because it was empty anyway. That layer turned out to be a symptom of a SQL injection vulnerability. This post is about the design that produced it — which I still think is a good design — and the four things I got wrong inside it. The setup A web GIS with about 2.7 million features: 1.8 million points, 697,000 lines, 172,000 polygons. Users create layers through the UI, upload data into them, edit geometry, and expect to see it on a map. The features do not live in a table per layer. They live in three tables — one for points, one for lines, one for polygons — with a layer_id foreign key and a JSON column for attributes: project_pointfeature 1,820,288 rows project_linefeature 697,009 rows project_polygonfeature 171,830 rows That's a deliberate trade. A table per layer means DDL every time a user clicks "new layer", a migration story that never ends, and a schema that drifts. Three generic tables mean one schema, one set of indexes, and layers that are just rows in a metadata table. The cost lands on the tile server. The pattern Martin serves vector tiles from PostGIS. Point it at a database and it discovers spatial tables and views and publishes each as an MVT endpoint. It can be told to publish views but not tables: postgres : auto_publish : from_schemas : [ public ] publish_tables : false reload_interval : 5s So: give every layer its own view. A Django post_save signal on the Layer model creates it: CREATE OR REPLACE VIEW t19_saobracajni_znakovi AS SELECT f . id , f . feature_attrs , f . geom , f . layer_id , l . name AS layer_name , lg . name AS layer_group_name , p . title AS project_title FROM project_pointfeature f JOIN project_layer l ON f . layer_id = l . id JOIN project_layergroup lg ON l . layer_group_id = lg . id JOIN project_project p
AI 资讯
EF Core bugs that look like correct code
Most EF Core bugs I've seen in production aren't from bad code. They're from code that looks right. It compiles, it passes review, it works fine locally against a database with twelve rows in it. Then it hits a table with five thousand rows, or a second replica, or a request that gets cancelled halfway through, and it falls over in a way nobody wrote a test for. None of the mistakes below are exotic. They're the default behavior of EF Core when you don't opt out of it, or the default behavior of a deployment when nobody thought about what "five pods start at the same time" actually means. Here's the setup I use and the list of ways it goes wrong if you skip a step. The entity namespace Sample.Domain.Posts ; public sealed class Post { public Guid Id { get ; private set ; } = Guid . CreateVersion7 (); // sequential → index-friendly public required string Title { get ; set ; } public required string Slug { get ; init ; } public string Body { get ; set ; } = string . Empty ; public DateTimeOffset ? PublishedAt { get ; private set ; } public Guid AuthorId { get ; init ; } public uint RowVersion { get ; set ; } // optimistic concurrency token public void Publish ( TimeProvider clock ) { if ( PublishedAt is not null ) throw new DomainException ( "Post is already published." ); PublishedAt = clock . GetUtcNow (); } } Two things here that are easy to skip and annoying to retrofit later. Timestamps are stored as UTC ( DateTimeOffset ), rendered in the user's timezone only at the edge — I do the same thing on ProcessHub, storing everything UTC and rendering in Asia/Tehran, because "what timezone is this in" is a much worse question to answer after the data already exists in three different formats. Second: the clock comes in as TimeProvider , not a call to DateTime.UtcNow buried inside the method. It's a small thing, but it's the difference between a test that can assert "publishing sets the timestamp to exactly this value" and a test that has to accept "sometime around now."
AI 资讯
How to Build a Fair A/B Audio Preview for AI Processing
Two audio players do not make a fair before-and-after test. If the second player restarts from zero or takes half a second to load, the user is no longer comparing two versions of the same moment. They are comparing two memories. That is a weak way to evaluate any audio effect. It is especially weak for AI processing. A denoiser can remove a fan while softening consonants. A de-reverb model can reduce the room tail while making the voice sound less natural. The output may be cleaner without being better. The preview therefore has one job: let the listener switch quickly enough to hear both the improvement and the damage. The rule I use is deliberately boring. Both versions should contain the same edit and play from the same position. Switching should not restart playback or create a pause. The interface should not hint that one version is supposed to win. Two independent <audio> elements fail surprisingly quickly. Each owns its playback state, buffering behavior, clock, and seek operation. The user ends up finding the same position twice and comparing one sound with a memory of another. A better interface has one transport and one version control: [ Play ] [ Original | Processed ] 00:18 ━━━━━━━ 00:42 The transport decides where playback happens. The segmented control decides which signal is audible. One transport, two signals For a short preview, I decode both files into AudioBuffer s, start them at the same AudioContext time and offset, and route each through its own GainNode . Both sources run; only one gain is open. decodeAudioData() decodes complete file data and resamples it to the context's sample rate. The decoded buffers can then share the same audio clock. See the MDN documentation for format and loading details. The core is small: const context = new AudioContext (); const originalGain = context . createGain (); const processedGain = context . createGain (); originalGain . connect ( context . destination ); processedGain . connect ( context . destination )
AI 资讯
One Missing Parameter Cost Me Six Hours (PortSwigger Lab)
I spent six hours trying to upgrade a non-admin user to admin, convinced I was missing some clever bypass. The gap turned out to be one field in a request body I'd already looked at twice. This is a PortSwigger lab on multi-step process access control. The setup: an admin panel with a user upgrade flow. You pick a user, hit upgrade, then confirm on a second screen before the change actually goes through. Not counting the admin login and accessing the admin panel, that's two steps. The goal was to login as wiener (my non-admin account) and upgrade it to admin without ever having admin access to begin with. Fig 1. A quick look at the admin interface in action. What I tried that didn't work I followed and wrote out the steps the admin flow actually takes, so I could inspect each step individually. Checked the change-email route for anything reusable. Tried hitting the admin and admin-roles paths directly with different HTTP methods. Added the referrer header with the value I'd seen during the legitimate admin flow. Went through the HTML and JS on every relevant page. Tried looking for where the user list was being fetched from. Tried the user-ID-in-params trick that had worked on an earlier lab. None of this brought any results and just got me more frustrated. The thing is, I was going at this problem with the assumption that in this scenario, I was a hacker with no idea of how the admin system actually worked when upgrading users. And that the lab giving me access to the admin credentials was just to hint towards any probable vulns. Why I slipped into that line of thinking, I have no idea. As I watched the hours tick by on my laptop clock, I grew increasingly aware of the painful fact that some LLM somewhere could probably one-shot this problem. That I could end my suffering by taking a knee before the mighty oracle called Claude. And you as a reader are probably wondering why I didn't submit. Well I was determined to actually learn. I had told myself going into this,
AI 资讯
Auto Subtitles Are Drafts: Why 99% Accuracy Isn’t the Finish Line
In one test clip, the auto subtitles looked almost perfect. Then one auto subtitle showed gp where the speaker had actually said HP . It was one token in a long transcript, and that was exactly the problem: nothing in the editor made it look more dangerous than the clean words around it. Disclosure: AI helped me edit and structure this article. The gp / HP mistake came from my own build, and I checked the technical details against the code and the working editor. I ran into this while building a subtitle editor. The ASR system already returned word-level timing and confidence values, but a polished block of text made every word look equally trustworthy. The model exposed uncertainty; the interface hid it. That led me to a narrower engineering conclusion: Auto subtitles are drafts. An accuracy score describes a model result; it does not define a finished review workflow. Why auto subtitles need more than one accuracy percentage Speech-to-text systems are often evaluated with word error rate , or WER. In its simplest form: WER = (substitutions + deletions + insertions) / reference words That is useful for comparing transcripts against a known reference. For auto subtitles, trouble starts when a model-level metric is turned into a product-level promise. Suppose a 100-word transcript contains one wrong word. Its word accuracy may look excellent. But a single auto subtitle can carry very different consequences: Changing “and” to “an” may be harmless. Changing a person’s name damages trust. Changing 15 to 50 changes the meaning. Changing HP to gp made my test caption look careless. Dropping “not” reverses the sentence. WER counts errors. It does not price their consequences. Good auto subtitles also depend on things that a transcript-only score does not fully describe: whether words appear at the right time; whether cue boundaries follow the sentence; whether a line is readable before it disappears; whether punctuation helps or hurts comprehension; whether the user knows
AI 资讯
Your AI Cover Art Looks Great — Until It's a Thumbnail
Every card in RealFeedApp has a cover image. They aren't fetched from the articles — publishers' images come with rights I don't have — so the app uses its own bank, generated ahead of time, one pool per topic. I generated that bank once, shipped it, and this month threw all of it away and started over. Twice over, actually, because there were two separate failures and only the first one was my fault in the obvious way. Failure one: I got exactly what I asked for The original prompt asked for a dark, moody look — very dark near-black background , generous negative space . In a preview grid at full size, the results were genuinely nice. Restrained, editorial, not the usual glowing-blue-circuit-board thing. Then I looked at the actual feed. Cards render as tiles a bit under 400 pixels wide. A small object floating in a large field of black, scaled down to a tile, is a black rectangle. Not a bad image — no image at all. The model had done precisely what I asked: it put a modest subject in a lot of empty darkness. Negative space is a compositional virtue at poster size and a bug at thumbnail size. The lesson is dull and I'll probably need it again: prompt for the size the image will be seen at, not the size you review it at. I was approving art in a grid of large previews and shipping it into small tiles, and never once compared the two. The rule that replaced the old one is three words long — brighter, higher contrast, subject filling the frame edge to edge. Failure two: the model started writing the news The rewrite asked for documentary photography. The first batch came back with something I didn't expect: several images contained a coloured band along the bottom of the frame with a headline in it. Invented words, mangled letterforms, confident typography. One read TROOLDOGS NEWS . Three images out of fifteen. Not a fluke, a pattern. The cause was in the prompt, and it was three words working together. I had asked for an editorial look, described the images as news f
AI 资讯
SSE in Go: Your Timeouts Do Not Apply Where You Think
An SSE stream is an HTTP request that never ends. Every default you did not touch is working against it. TL;DR : your SSE endpoint breaks twice before it reaches your logic. Once because the Connection header is illegal in HTTP/2. Once because your Go server's default timeouts cut the stream at 30 seconds. And if you stay on HTTP/1.1, a permanent stream freezes the rest of your page. In August 2026, Go patched a flaw where a timeout was not applied to HTTP/2 connections. Same lesson: a timeout only protects what it covers. This article is for Go developers shipping streaming to production. SSE, WebSocket, long-poll: anything that stays open. The setup SSE stands for Server-Sent Events. It is a one-way HTTP stream. The server pushes messages, the browser listens. The format is simple. You open a text/event-stream response, you write lines, you flush. The browser receives them as they come. I run two SSE endpoints in production. The first is a Go notification service, on Kubernetes, behind a reverse proxy. The second is an internal cockpit that refreshes its UI without a page reload. Both broke. In different places, with the same symptom. An SSE stream is a request that never ends Here is the key to the whole article. To your server, an SSE stream is not a special case. It is a very slow request. And every guardrail in an HTTP server targets the slow request. Write timeout, context timeout, idle timeout. They exist to kill whatever drags on. Your legitimate stream looks exactly like what they are meant to kill. That is the whole problem. The Connection header is illegal in HTTP/2 First incident. The endpoint answers 200, then the browser shows net::ERR_HTTP2_PROTOCOL_ERROR . The client reconnects in a loop. The cause was one line. My handler set a Connection: keep-alive header. We all copy it from some old SSE tutorial. Connection is a hop-by-hop header. A hop-by-hop header applies to one network hop only, never end to end. HTTP/2 forbids these headers (RFC 9113 §8.2.
AI 资讯
The Evolution of Web Forms — Part 3
The Evolution of Web Forms — Part 3: React Hook Form, Validation Libraries, and Zod In Part 2, we learned that React solved the problem of manually updating the DOM. Instead of writing: emailError . textContent = " Email already exists " ; emailInput . setAttribute ( " aria-invalid " , " true " ); React allowed us to describe the interface from state: < input aria-invalid = { Boolean ( errors . email ) } /> { errors . email && ( < p > { errors . email } </ p > )} However, React did not automatically manage: Form values Validation errors Touched fields Dirty fields Submission state Reset behavior Dynamic fields Backend errors Performance Developers still had to build those features manually. That created the need for form-management libraries. This part covers: React Hook Form’s philosophy and architecture React Hook Form’s core APIs Validation libraries React Hook Form with Zod and TypeScript By the end, we will build a production-style registration form using: React + TypeScript + React Hook Form + Zod + An API layer Stage 9: React Hook Form Deep Dive React Hook Form is not simply a shorter way to write controlled React forms. It uses a different architectural philosophy. A traditional controlled input stores its value in React state: const [ email , setEmail ] = useState ( "" ); < input value = { email } onChange = { ( event ) => { setEmail ( event . target . value ); } } /> Every keystroke produces a state update: User types ↓ onChange runs ↓ setEmail runs ↓ Component renders again ↓ Input receives the new value React Hook Form prefers native, uncontrolled inputs when possible. < input { ... register ( " email " ) } /> The browser stores the current value inside the input element. React Hook Form registers the input, listens to its events, tracks relevant form state, and reads its value when required. React Hook Form’s official documentation describes register() as the mechanism that connects an input to validation, value tracking, and submission. Controlled vers
AI 资讯
Your canvas.toBlob might be silently handing you a PNG
A user told me the .webp files my tool produced wouldn't open on their desktop. I opened one in a hex editor. First four bytes: 89 50 4E 47 . It was a PNG. With a .webp extension. The encoder wasn't broken. I had simply never checked whether the browser actually did what I asked. The spec says it's allowed to do this Here's the code. Nothing looks wrong with it: canvas . toBlob ( blob => { download ( blob , ' output.webp ' ); }, ' image/webp ' ); The callback fires. The blob isn't null. Its size looks reasonable. Everything succeeds — except it isn't WebP. This is not a bug. The HTML spec explicitly requires it: if the user agent doesn't support the requested type, it must create the file using the PNG format instead. No exception, no warning, no second argument telling you what happened. There's exactly one place that information exists — blob.type : canvas . toBlob ( blob => { console . log ( blob . type ); // iOS below 16.4: "image/png" }, ' image/webp ' ); toDataURL does the same thing, but at least there the fallback is visible to the naked eye, since the data URL literally starts with data:image/png;base64, . There is no capability query for this My first instinct was to special-case iOS. That falls apart quickly. Every browser on iOS is WebKit underneath, so "is this Safari" isn't a meaningful question. Embedded webviews inside apps track the system version in ways that don't always match the standalone browser. And a user can flip on "Request Desktop Website" and hand you a macOS user agent from an iPhone. More fundamentally: the user agent string answers "who are you" , and I need to know "can you encode WebP right now" . Between those two questions sit the engine version, OS version, host app, and build flags. Any mismatch in that chain and your lookup table lies to you. So I went looking for an official capability API. Media has them: MediaRecorder . isTypeSupported ( ' video/webm;codecs=vp9 ' ); // → boolean await navigator . mediaCapabilities . encoding
AI 资讯
How Particle Effects Improve Game Feel in HTML5 Games
A game can be mechanically correct and still feel flat. The button works. The enemy loses health. The coin counter increases. The level completes. Everything technically functions, but the player's actions do not seem to have much weight. Particle effects are one of the cheapest ways to fix that. Not because every screen needs fireworks, but because particles give actions a visible consequence. Feedback Should Happen Immediately Imagine tapping an enemy in a mobile game. Version A: tap enemy HP decreases Version B: tap small flash impact particles enemy reacts HP decreases The underlying mechanic is almost identical. The second version communicates the result more clearly. The player sees exactly where the hit happened. That matters on mobile screens where fingers frequently cover part of the action. Particles Can Explain the Game VFX is not only decoration. It can communicate state. Damage Particles show where an impact happened. Healing A slow upward effect can visually separate healing from damage. Selection A subtle glow or ring can show which object is active. Currency Particles moving toward a counter connect the collected object with the UI value that changed. Cooldowns A burst or dissolve can show that an ability has become available. Danger Smoke, sparks, or unstable energy can communicate that an object is close to breaking. Good VFX helps the player understand the game without another label or tutorial popup. Timing Matters More Than Particle Count A common mistake is assuming better effects need more particles. They usually need better timing. Consider a button press. You could emit 100 particles over two seconds. Or you could emit 12 particles exactly when the interaction occurs. The second effect will often feel better because it reinforces the player's action. For responsive games, the sequence might look like this: 0 ms input 0 ms visual response begins 20 ms burst expands 80 ms largest particles appear 200 ms effect begins disappearing 350 ms effect