AI 资讯
Bifrost AI Gateway Would Have Saved My App
I was showing ChefExtract to some friends when it started returning error 404 for specific operations linked to AI. After some embarrassment, I went home to figure out the problem: The model I used for those specific operations had been deprecated. And just like that, my app was failing. But I learned my lesson: relying entirely on one API creates a single point of failure (not rocket science). The easiest fallback mechanism would be to rely on a backup model. But these increase maintenance, and it doesn’t scale well. A better fix relies on AI gateways. What is an AI gateway? An AI gateway is a middleware layer that sits between your application and the LLM providers. Instead of your code calling OpenAI or Anthropic directly, it calls the gateway, and the gateway forwards the request. Concretely, a gateway buys you four things: One API for many providers. Write your code once, switch between GPT, Claude, Gemini, or a local model without rewriting anything. Automatic failover. If your primary provider fails or deprecates your model (as it happened to me), requests reroute to a backup. Users never see a 404. Cost control and caching. Budgets, rate limits, and cached responses for repeated queries, enforced in one place instead of scattered across your codebase. This is especially useful when relying on models from different providers. Observability. Every request is logged, timed, and priced, so “why is our AI bill so high?” becomes a query instead of an investigation. Once again, this is especially useful when dealing with multiple models from different providers. This is exactly what my app needed. Failover alone would have turned my deprecated-model incident into a non-event. Enter Bifrost There are many AI gateways, but eventually I explored one called Bifrost because it is open source and you can see how it operates under the hood. Bifrost is an open-source AI gateway built by Maxim AI and written in Go. Bifrost bridges your app to more than 20 providers: OpenAI,
AI 资讯
React Performance Optimization Techniques That Actually Work
Performance optimization in React is often surrounded by myths. Developers routinely wrap every single component in React.memo , wrap every function in useCallback , and wonder why their application is still sluggish or memory-heavy. Premature optimization can actually degrade app performance and clutter your codebase. To build fast React applications, you need techniques that address actual bottlenecks: unnecessary re-renders, unoptimized state placement, oversized bundles, and main-thread blocking. Here are five practical React performance optimization techniques that deliver measurable results in production. 1. Push State Down (Fix Rerender Cascades) Before reaching for useMemo or React.memo , evaluate your state placement . When state lives too high up in the component tree, every state update forces the entire sub-tree to re-render. ❌ The Anti-Pattern: State at the Root // Changing `color` forces <HeavyChartComponent/> and <ComplexTable/> to re-render! export default function App () { const [ color , setColor ] = useState ( ' #6366f1 ' ); return ( < div > < input type = "color" value = { color } onChange = { ( e ) => setColor ( e . target . value ) } /> < p style = { { color } } > Sample Text </ p > < HeavyChartComponent /> < ComplexTable /> </ div > ); } ✅ The Fix: Component Isolation Move the isolated state and its control into its own dedicated child component: Javascript function ColorPicker () { const [ color , setColor ] = useState ( ' #6366f1 ' ); return ( < div > < input type = "color" value = { color } onChange = { ( e ) => setColor ( e . target . value ) } /> < p style = { { color } } > Sample Text </ p > </ div > ); } export default function App () { return ( < div > < ColorPicker /> { /* These components are no longer impacted by color state changes */ } < HeavyChartComponent /> < ComplexTable /> </ div > ); } 2. Pass Components as Children (Component Composition) Sometimes state must remain in a parent component, but you don't want child components
AI 资讯
Solon Cloud: The Distributed Toolkit That Doesn't Lock You In
When I first looked at Solon Cloud, I expected another opinionated microservice framework—the kind that tells you exactly which registry, which config center, and which message queue to use. What I found instead was a different philosophy: a set of interface standards with swappable plugin implementations . You write your code against the interfaces, and switching from local development to production Cloud is a YAML change, not a code rewrite. Let me walk through how it works. The Core Idea: An Anti-Corruption Layer Solon Cloud isn't a single product. It's a collection of 13 service interfaces backed by a plugin ecosystem. The official docs call it a "通用防腐层" (general anti-corruption layer), and the name fits. Here's the architecture: Your Business Code ↓ (uses CloudClient or annotations) ┌─────────────────────────────────────┐ │ Solon Cloud Interfaces │ │ (CloudConfigService, CloudEvent, │ │ CloudDiscoveryService, ...) │ ├─────────────────────────────────────┤ │ Plugin: local │ Plugin: water │ │ Plugin: nacos │ Plugin: consul │ │ Plugin: ... │ │ └─────────────────────────────────────┘ Your code depends on the interfaces. The plugins implement them. You swap the dependency and the YAML config—the code stays untouched. The 13 Service Interfaces From the official family page, Solon Cloud defines these capability interfaces: Interface Purpose CloudConfigService Distributed configuration CloudDiscoveryService Service registration & discovery CloudEventService Distributed event bus CloudFileService Distributed file storage CloudI18nService Distributed i18n CloudIdService Distributed ID generation CloudJobService Distributed scheduled jobs CloudListService Distributed whitelist/blacklist CloudLockService Distributed locking CloudLogService Distributed logging CloudMetricService Distributed metrics CloudTraceService Distributed tracing CloudBreakerService Circuit breaker Each interface has a corresponding configuration namespace ( solon.cloud.@@.xxx ) and a set of plugin im
AI 资讯
Building a Browser-Based Voxel Editor with React Three Fiber
I have been building VoxelDraft , a voxel editor that runs entirely in the browser without an account or installation. The editor supports block painting, layers, keyframe animation, GIF recording, local projects, and exports for OBJ/MTL, GLB, VOX, Minecraft Schematic, and Roblox RBXL. This post covers the architecture choices that kept those features manageable. Keep edit data serializable The editable model is an array of plain voxel records rather than a collection of Three.js objects: type VoxelData = { position : [ number , number , number ] color : string layerId ?: string } That decision makes JSON backups, local persistence, undo/redo snapshots, sharing, and format conversion much simpler. Three.js objects are derived render state, not the source of truth. Render repeated cubes with InstancedMesh Creating one mesh and one React component per cube becomes expensive as a model grows. VoxelDraft uses THREE.InstancedMesh where geometry and material can be shared. Each voxel contributes a transform matrix. Pointer intersections return the instanced mesh and instance ID, which can be mapped back to the editable voxel record. There are tradeoffs. Per-voxel colors need instance colors or grouping by material, and changing a single block still requires carefully updating the instance buffers. The reduction in draw calls is worth that complexity. Make exporters independent from UI The format exporters accept voxel records and produce a Blob . The UI is only responsible for validation and triggering a download. const blob = exportToVOX ( voxels ) const url = URL . createObjectURL ( blob ) VOX, Minecraft Schematic, and RBXL are generated directly. For GLB, the app builds a temporary Three.js scene and sends it to GLTFExporter from three-stdlib . Keeping binary generation separate from React event handlers makes exporters easier to test and reuse. Move GIF encoding off the main thread VoxelDraft records both animation output and modeling timelapses. GIF encoding can easi
AI 资讯
Stop Asking AI for Test Cases: Building a Gate-Controlled SDET Prompt
How to Get the Maximum Value Out of This Framework Having built and iterated on this prompt through multiple production edge cases, here are the exact execution strategies I recommend depending on your workflow: 1. The Human-in-the-Loop Workflow (Recommended for Chat UI) Run it in two separate chat threads: Don’t let long conversation history degrade your test accuracy. Run Phase 1 in Thread A to get your gap analysis and critical questions. Review the gaps, clarify what you can, and then update your original requirement text. Start Thread B for Phase 2: Open a fresh conversation, paste the updated requirements + this framework, and jump straight into generation. This completely eliminates context drift and keeps the LLM laser-focused on state mutation rules. 2. The 2-Pass Programmatic Auditor (For Automated CI/CD Pipelines) If you’re calling an LLM via API or integrating this into a pre-commit GitHub Action, split the execution into two isolated passes: Pass 1: Run Phase 1 & 2 to generate the initial test table. Pass 2 (The Audit Pass): Feed the generated table into an isolated, secondary prompt whose only job is to enforce the Verification Check (verifying exact boundary literals, API status codes, and non-mutation assertions). Separation produces drastically higher assertion reliability than asking a model to self-audit in a single turn. 3. How to Live-Demo or Teach This For Live Streams & YouTube: This framework makes for a high-signal live demo. Paste an intentionally ambiguous user story (e.g., a webhook handler or payment endpoint), watch Phase 1 halt at the gate live, discuss the surfaced edge cases on camera, reply PROCEED, and review the generated DEFERRED risk rows. It shifts the content focus from “Look at this cool AI tool” to “This is how Senior SDETs think about systems.” For Technical Writing & Post-Mortems: The progression from a naive “write me test cases” prompt to a strict 2-phase state-machine framework is a technical narrative in itself. Break
AI 资讯
A Checklist When You're Stuck
I was two hours into a bug and completely certain it was mine. Properties I'd added on the Java side of an application weren't showing up on the JavaScript side. I'd just touched that code. It had to be my change — that's not a hunch, that's just how these things go, you break the thing you were last inside of. I spent the better part of an hour re-reading my own diff, convinced the answer was somewhere in it, because it obviously had to be. It wasn't in my diff. It was a legacy codegen sync script, three steps removed from anything I'd touched, quietly failing to invalidate an old artifact. I didn't find that out by getting smarter. I found it out by walking away from my own certainty, twice, guided by a checklist I'd set long before I ever opened that file. Here's that checklist, the same one every time. I go through this checklist anytime I'm about to dig into a problem that I know might be tricky. Before you start — while you're calm, not while you're stuck — decide how long you're willing to work under pressure before you're required to stop. I typically set this at two hours . Decide what activity you'll do when the timer goes off. Something calibrated to wherever you happen to be that day: a walk or a coffee run at the office, cooking dinner or picking up a controller at home. Set the timer and get to work. When the timer goes off, stop. Immediately. No snooze button, no "just five more minutes," especially when you feel close. Get up and perform the activity from step two. Then loop back to step three. If the day's ending and the work isn't done, "call it done for today" and return to the checklist tomorrow. That's the whole thing. It reads like it belongs on a sticky note, and I want to own that up front instead of pretending it's more sophisticated than it looks. Why you need a plan instead of just trying harder I built this checklist to get unstuck. What it's actually for is disrupting confirmation bias, and I didn't fully understand that until I'd used i
AI 资讯
What's the smallest, dumbest thing that made you completely lose trust in an AI agent mid task?
It doesn't even have to be a big dramatic failures, more the small moments where something clicked and you went from trusting the output by default to double checking everything. For me it was watching an agent confidently rename a function across twelve files, then leave the original function untouched in a thirteenth file it apparently didn't search, with zero indication anything had been missed. It wasn't even a hard case, the file just wasn't in the directory it happened to grep first. What was your moment? And did it actually change your workflow afterward , or did the trust creep back in after a week like it always seems to for me?
AI 资讯
SOLID Principles Cheat Sheet
Writing software that scales from a small monolith into a multi-team distributed system requires strict architectural discipline. The SOLID principles —coined by Robert C. Martin ("Uncle Bob")—serve as fundamental guidelines for object-oriented design and system architecture. When improperly understood, developers often fall into two extreme traps: creating monolithic "God objects" that break with every change, or over-engineering systems into hyper-fragmented, unmaintainable micro-services. In this deep-dive guide, we will break down each of the 5 SOLID principles from low-level class design up to high-level distributed systems design, complete with bad vs. refactored Java examples, system architecture diagrams, trade-off analyses, and a comprehensive cheat sheet. SOLID Principles Cheat Sheet Principle Core Concept Anti-Pattern / Code Smell Refactoring Solution Single Responsibility (SRP) A class or module should have one, and only one, reason to change (serving one business actor/domain). God Class / Micro-Fragmentation: Classes handling payment, DB, and notifications, OR over-fragmented single-function classes. Split by domain responsibility. Use orchestrator/coordinator components for workflows. Open/Closed (OCP) Software entities should be open for extension, but closed for modification . Conditional Bloat: Cascading if-else or switch statements checking object types or channels. Strategy Pattern, Dependency Injection, and Event-Driven Pub/Sub messaging (e.g., Kafka). Liskov Substitution (LSP) Subtypes must be completely substitutable for their base types without breaking client behavior. Runtime Exceptions: Subclasses throwing UnsupportedOperationException or silently breaking logic. Split fat inheritance hierarchies into granular, capability-specific interfaces. Interface Segregation (ISP) No client should be forced to depend on methods it does not use. Fat Interfaces: Monolithic interfaces forcing callers to mock or implement irrelevant methods. Role-focused
AI 资讯
Hugging Face Has a Deepfake Nudes Problem
Researchers tested top image editing models on Hugging Face and found they could easily create explicit deepfakes—and 1,000 image editing prompts show how people use the software.
开发者
Microsoft lays out a buffet of Windows goodies for JavaScript Developers
submitted by /u/stronghup [link] [留言]
AI 资讯
Without Exception: How Neander Programs Fail
Neander has no exceptions. No try , no catch , no finally . A call to one of the host application's APIs returns something closer to Rust's Result : either the answer, or the reason there is no answer. In place of a catch block there is one type marker, three operators, and a guarantee that every submission comes back in the same shape no matter what happened. Last time the foundational series closed with isolation. This is the first of two encores, and it takes the subject that came up in nearly every entry without ever being laid out in full: what happens when something goes wrong. There are two answers, because there are two audiences. An error is a value while the program runs, and a verdict once it has stopped. The two are made of the same parts, on purpose. The failable type Every call returns a failable type, written T! . It carries either a value of type T or an error with a code, a message, and the name of the function that produced it. T! is the mirror of the nullable type T? . Same shape, different question: one asks whether a value is there at all, the other asks whether obtaining it worked. The mirroring runs deeper than the notation, because the same three operators serve both types. A failure gets no unwrapping vocabulary of its own. Those three are =? , ?? and is : // narrow, or throw the error out of the enclosing block let order : Order =? call orders .get ( id : 42 ) // or substitute a default let order : Order = call orders .get ( id : 42 ) ?? emptyOrder // or inspect it and decide let result : Order! = call orders .get ( id : 42 ) if result is error { if errorCode ( result ) != 404 { throw result } return emptyOrder } A standalone call statement, one without a let , narrows implicitly: the error is thrown and the success value is discarded. One property does the heavy lifting throughout the rest of this post: T! originates only from a call . No expression picks up a ! along the way, and no widening rule introduces one. The marker means exactly o
AI 资讯
The Day My AI Taught Me That Passing Tests Means Nothing
I never set out to build VentureTwin AI as just another chatbot. The idea was much bigger than answering questions. I wanted to build a digital twin that could understand a student's entire journey—their projects, certifications, technical skills, academics, achievements, and career interests—and use all of that to provide meaningful career guidance. Instead of simply recommending jobs based on keywords or certificate counts, I wanted the system to answer a much harder question: What is this student actually good at, and where are they most likely to succeed? To make that possible, I designed the platform as a collection of independent intelligence modules. The Certificate Intelligence module retrieved and verified certifications. Resume Intelligence evaluated technical skills and experience. Project Intelligence analyzed project metadata such as technology stack, complexity, implementation, and impact. Each module produced its own output, which was then passed to a scoring engine that generated a Career Readiness Score. Individually, every module worked exactly as expected. Then I compared two student profiles. The first student had completed more than 20 online certifications but had only a couple of basic projects. The second student had fewer certifications, but had built full-stack applications, worked with AI models, contributed to open-source projects, and actively participated in hackathons and technical competitions. I expected the second profile to receive stronger recommendations. It didn't. Instead, the student with the larger collection of certificates consistently received the higher Career Readiness Score. At first, I assumed something was broken. I traced every stage of the scoring pipeline, inspected API responses from every module, verified the PostgreSQL records, and even recalculated the scores manually. Every value matched. Every API response was correct. The database contained exactly what it should. The scoring engine was behaving exactly as I
开发者
React 19's useActionState Showed Me Why Disabling My Submit Button Was Never Enough
Every form I ever shipped before React 19 needed the same three pieces of state, and I wired them up...
AI 资讯
What Is Agentic Marketing? How AI Agents Are Replacing the Modern Marketing Stack
According to Gartner, by 2026, 80% of enterprise marketing organizations are expected to use...
AI 资讯
[Advanced Rust] 1.12. Lifetimes (Advanced) Pt.2 - Lifetime Variance, Covariance, Invariance, Contravariance
1.12.1. Lifetime Variance Variance is a concept in Rust’s type system. It describes how generic parameters — especially lifetime parameters — relate to one another in the type hierarchy. We can think of it simply as variance describes which types are “subtypes” of other types , where “subtype” is somewhat similar to the concept used in Java and C#. In addition, variance also cares about when a “subtype” can replace a “supertype” and vice versa . In general, if A is a subtype of B, then A is at least as useful as B. Here is a Rust example: if a function takes &'a str , then &'static str can be passed in. Because 'static is a subtype of 'a , 'static lives at least as long as any 'a (and 'static can remain valid for the entire program). 1.12.2. Three Kinds of Lifetime Variance All types have variance. The variance associated with each type defines which similar types can be used in that type’s position. Note: the following content is fairly difficult. It is recommended that you first recall the ideas of sufficient conditions and necessary conditions from high school math. 1. Covariant Covariant means that a type can be replaced only by a “subtype.” Covariance means: if A <: B (A is a subtype of B), then F<A> <: F<B> (F<A> is also a subtype of F<B>) This is a transitive inheritance relationship from smaller to larger , similar to reasoning from a sufficient condition : if A holds, then B must also hold (A is a sufficient condition for B). For example, &'static T can replace &'a T , because &T is covariant over the lifetime 'a , so 'a can be replaced by one of its subtypes, such as 'static . 2. Invariant Invariant means that you must provide the exact specified type. Invariance means: A <: B cannot imply F<A> <: F<B>, and F<B> <: F<A> also cannot be inferred This means there is not enough relationship between F<A> and F<B> to derive one from the other, so they are neither sufficient conditions nor necessary conditions ; they are independent. For example, the mutable refe
AI 资讯
[Advanced Rust] 1.11. Lifetimes (Advanced) Pt.1 - Review, Borrow Checker, Generic Lifetimes
1.11.1. Review In the beginner tutorial, we mentioned that every reference in Rust has a lifetime. A lifetime is the scope in which the reference remains valid, and in most cases it is implicit and inferred by the compiler. When you take a reference to a variable, the lifetime begins. When the variable is moved or goes out of scope, the lifetime ends. In other words, for a reference, a lifetime is the name of the code region in which it must remain valid. Lifetimes usually overlap with scopes, but not always. 1.11.2. Borrow Checker Whenever a reference with some lifetime 'a is used, the borrow checker checks whether 'a is still alive. The process is: Trace the path back to where 'a began — that is, where the reference was obtained From there, check whether there are conflicts along that path Ensure that the reference points to a value that can be accessed safely This example uses the rand crate. Add the following dependency to Cargo.toml : [dependencies] rand = "0.8" Consider this example: use rand :: random ; fn main () { let mut x = Box :: new ( 42 ); let r = & x ; if random :: < f32 > () > 0.5 { * x = 84 ; } else { println! ( "{}" , r ); } } x is of type Box<i32> Declaring r as a reference to x means the reference’s lifetime begins on that line (line 5) On line 7, the value of x is modified through dereferencing. That requires a mutable reference to x . At this point, the borrow checker looks for a mutable reference to x and checks whether its use conflicts with anything else. In this example there is no conflict, so the code is valid You may ask: line 7 is inside the scope of r . Since *x needs a mutable reference to x , shouldn’t having both the immutable reference r and the mutable reference *x in the same scope violate the borrowing rules and produce an error? In fact, Rust is smart enough to know that if the if branch is taken, the else branch cannot be taken. r is never used in the if branch at all, so using the mutable reference *x in the if branch is fine
开发者
How Cursor + BrowserAct Handles Dynamic Pages Without Brittle Selectors
TL;DR Modern web applications change constantly. Components are re-rendered, generated...
AI 资讯
Picking a text-to-image API for a SaaS app: REST, pricing, and safety
If you just want the recommendation: call a plain REST image generation endpoint from your Node.js backend, keep the prompt-in / image-out path as dumb as you can stand, and add a chat model on top only when you actually need policy checks or structured prompts. For a first text-to-image feature inside a SaaS app, that is the entire architecture worth building. I've shipped that feature twice. Both times the generation call was the boring part. What ate the calendar was everything around it: deciding whether the output was safe to show a paying customer, reading the licence terms closely enough to know we could put generated art in a customer's exported PDF, storing the result somewhere that wasn't the provider's temporary URL, and — the part I got wrong, which I'll come back to — making retries safe. I run a one-person company, so I optimise for the number of moving parts I have to keep in my head at 2am, and a text-to-image feature that pulls in three new vendors is a feature I'll quietly regret. Your priorities may be different if you have an infra team. What should I look for in a text-to-image API for a SaaS app? Four things, in the order they'll actually hurt you. Model availability in your regions comes first. If you sell into both the US and the EU, check that the model you pick is served in both, because "we support Europe" sometimes means the marketing site and not the inference region. Ask for it in writing if the answer matters to your DPA. Commercial use terms come second, and they're the ones nobody reads until legal asks. Most of the big image models now permit commercial use of outputs, but the details differ on who owns the output, whether you can train on it, and what happens with likenesses and trademarks. Read the actual terms page for the model, not the aggregator's summary of it — aggregators route to several vendors and the upstream licence is what governs your PDF. Then pricing shape. Per-image billing is easy to model in a spreadsheet; per-s
科技前沿
France Records Its First-Ever Pyrocumulonimbus Cloud Amid Record-Smashing Fires
Extreme fire conditions on the ground have created unprecedented conditions in the atmosphere.
AI 资讯
One small AI workflow that solved our stale API documentation problem
One of the most annoying parts of backend development isn't building APIs. It's keeping the...