今日已更新 302 条资讯 | 累计 37551 条内容
关于我们

标签:#Java

找到 1192 篇相关文章

开发者

Nobody Designs for 2G. Here's What Building in Kenya Taught Me About "Fast" Websites

Most performance advice online assumes a baseline that doesn't exist for most of the world. Fast wifi, a recent phone, a stable connection. Lighthouse scores optimized for conditions half the planet doesn't have. I build web products for businesses in Kenya. A meaningful share of my users are on 3G, sometimes 2G, often on a budget Android phone with limited storage and a browser that hasn't seen an update in a year. Here's what that actually changes about how you build. Your bundle size is a business decision, not a dev preference A 2MB JS bundle that loads instantly on your MacBook can take 15 to 20 seconds on a real 3G connection. That's not a slow load, that's a user who left before your app finished parsing. I've watched analytics confirm this directly, drop-off spikes exactly where bundle size peaks. Skeleton screens matter more than animations Every extra animated transition is more work for a weak CPU to render. I stripped most micro-interactions out of a recent build and page-perceived speed improved more than any code-splitting change I made that month. Motion is a luxury feature for people with headroom to spare. Offline isn't an edge case, it's Tuesday Connections drop mid-session constantly, not from bad code, just from the actual infrastructure. If your app throws away form state on a dropped connection, you're actively costing your users. Basic local persistence before submission became a non-negotiable for me after watching real users lose an entire booking form to a 4 second network blip. Images are still the biggest offender in 2026 Everyone optimized images years ago and moved on. They didn't. I still regularly find production sites shipping unoptimized hero images at 3 to 4MB. On a fast connection that's invisible. On the connections a huge share of the world actually uses, that single image can be the whole page load. The real point "Fast" isn't a Lighthouse score. It's whether the app actually works for the person holding the phone it's meant fo

2026-08-09 原文 →
AI 资讯

How to Convert Files in the Browser Without Uploading Them

Most file-conversion workflows start with a trade-off that is easy to miss: Choose a file from your device. Upload it to a third-party server. Wait for processing. Download a new file. Trust that the original and the result are handled exactly as promised. That model is convenient, but it is not the only option. For a growing set of formats, a modern browser can read, transform, and export files directly on the user's device. The result is a different kind of tool: no upload queue, no account requirement, and no server-side conversion step. This post explains how browser-based file conversion works, where it is a strong fit, where it is not, and how we approach the problem in I Hate Converter , a free collection of locally run file converters. What “no upload” should mean “No upload” should be more than a reassuring line next to a file picker. For a browser converter, the useful promise is that the selected file is read and processed within the browser runtime. A tool can use browser APIs such as File , Blob , ArrayBuffer , Canvas , and Web Workers, as well as locally loaded WebAssembly modules, without sending the source file to an application server. That matters when a file contains information you would rather not place in another system: draft documents, customer exports, source assets, screenshots, scanned records, or internal media. It also reduces friction for quick conversions: choose a file, process it, download the result. The distinction is important: an app can have a website while still keeping the actual conversion local. A page load may fetch its code and assets, but the chosen file does not need to become a network request. Our no-upload file converter hub is built around that boundary: supported conversions run on-device, and formats that require a server are not presented as if they were local. The browser capabilities that make this possible Browsers are no longer just document viewers. Several stable platform features make useful local conversio

2026-08-09 原文 →
AI 资讯

Unary gRPC on Reactor Netty: Event Loop Serialization, Trailers, and Cancellation

With protocol values and message framing complete, Stage 2 delivered the first end-to-end call: plaintext h2c unary RPC. This is already on main , and Stage 3 and Stage 4 subsequently completed all four RPC cardinalities on the same transport primitive. Previous: Building a Leak-Safe gRPC Frame Decoder on Reactor Netty Method Descriptor Is Where Protocol Meets Types A method requires a precise service name, method name, cardinality, and request/response marshallers: var echo = new GrpcMethod <>( "testing.EchoService" , "Echo" , GrpcMethod . Cardinality . UNARY , new ProtobufMarshaller <>( StringValue . parser ()), new ProtobufMarshaller <>( StringValue . parser ())); The generated path must be: /testing.EchoService/Echo The service registry matches by exact full path. An unknown path returns UNIMPLEMENTED ; registering the same path twice fails immediately when building the service definition. Server Validates Protocol Before Subscribing to Business Logic ReactorGrpcServer uses Reactor Netty h2c: DisposableServer bound = HttpServer . create () . host ( host ) . port ( port ) . protocol ( HttpProtocol . H2C ) . handle ( handler: : handle ) . bindNow ( Duration . ofSeconds ( 10 )); Incoming requests are validated in order: HTTP method must be POST; content-type must be application/grpc or application/grpc+... ; te must declare trailers; path must exist; currently only unary cardinality is allowed; metadata and message size must not exceed limits. Only after validation passes does it create a GrpcCallContext and subscribe to the request body, preventing invalid requests from entering the business handler. HTTP 200 Does Not Mean RPC Success The server writes a compatible content-type first; the final status comes from trailing headers: response . status ( 200 ) . header ( HttpHeaderNames . CONTENT_TYPE , "application/grpc+proto" ); response . trailerHeaders ( trailers -> { GrpcException error = terminal . get (); if ( error == null ) { writeStatus ( trailers , GrpcStatu

2026-08-09 原文 →
AI 资讯

Building a Production AI Agent in Spring Boot: The LLM Judge That Scores Your Agent (Part 8)

Last week I ran a demo of the agent for a colleague who was deciding whether to bet a feature on it. The approval gate from Part 7 worked exactly as designed. The agent searched, added to cart, asked for the address, and stopped at the confirmation link. My colleague nodded and asked one question: "OK, but is it actually good?" I did not have an answer. I had 31 passing tests from Part 6, which prove the agent is bug-free. I had a state machine, which proves it cannot place an order without a human. Neither of those proves the agent answers customers well. A bug-free agent can still tell a customer the shop ships within two days when the shipping partner takes five. No unit test catches that, because no unit test reads the answer. That is the gap this part closes. Part 7 ended with a promise: the next part would build an evaluation harness, so "is it good" stops being a feeling and becomes a score. This is that part. I built an LLM-as-a-judge harness for the same e-commerce agent as Parts 1 through 7: same nine tools, same supervisor, same memory. It runs 40 real conversations from production logs against five metrics every night and prints a score for each one. The first run was uncomfortable, and that is exactly why it exists. I am a Senior Software Engineer II at BS23 in Dhaka, and I have been building production AI agents with Spring Boot and Spring AI for over a year. Everything below is the harness as I actually run it. The Difference Between Tested and Good Part 6 tested the agent without an LLM: 31 tests, zero model calls, asserting on tool calls, services, and the order state machine. That suite answers "did the agent call the right tool, in the right order, with the right arguments?" It cannot answer "was the answer right?" because you cannot write an assertion for an LLM's wording. Evaluation is a different layer. You cannot assert on the answer, but you can judge it, and you can use an LLM to do the judging. Spring AI documents this pattern in its LLM-as

2026-08-09 原文 →
AI 资讯

One Checkbox, Three Kinds of State in a Chrome MV3 Extension

I thought I had a settings bug. What I actually had was three different kinds of state pretending to be one boolean. While building a Chrome Manifest V3 email-tracker blocker, I expected a simple flow: you flip Gmail on in the settings, and the extension starts working in Gmail. That was the theory, anyway. The problem showed up when I was testing on a second Chrome profile. I'd enabled Gmail on my main profile, and Chrome Sync helpfully carried that preference over to the other one. But the optional permission for mail.google.com didn't come along — host grants live in the local profile and never sync. Profile number two now believed Gmail was enabled while lacking the host grant needed to inject the inbox content script or inspect its DOM. Depending on how you write your code, that's either a silent no-op or an extension quietly behaving as if access exists when it does not. Neither is great. Once I stopped and wrote it down, the picture got clearer. There are three separate things here: the inbox the user wants enabled, the host access Chrome has actually granted in this profile, and the dynamic DNR rules that are currently installed . Collapsing them into one flag is convenient. It's also wrong. The manifest is a menu, not an order The extension declares each webmail origin under optional_host_permissions . Every inbox gets activated on its own, and Chrome only asks the user for access when they turn that particular integration on. Here's the thing I had to internalize: declaring an optional origin means nothing by itself. Until the live grant exists, the extension has no business registering a content script for that inbox, poking at its DOM, or — by its own scoping policy — activating client-scoped blocking rules for it. Why bother with per-inbox prompts at all? Mostly trust. A tracker blocker that asks for all your webmail up front looks exactly like the thing it's supposed to protect you from. Asking for Gmail when you enable Gmail — and nothing more — is an

2026-08-09 原文 →
AI 资讯

I built OneToolBox — free browser-based tools for developers

Hey devs👋 I've been building OneToolBox : https://onetoolbox.dev/ It's a collection of free web utilities for developers and creators — JSON tools, YAML validation, hash generation, text diffing, image tools, converters, and more. The main idea is simple: do as much as possible directly in the browser, without requiring accounts or uploading users' files/data to a server. I'm still actively improving it, and I'd really appreciate feedback from developers here. What would you improve? Which tools are missing? Are there tools you use regularly that you'd like to see added? Any UX problems or annoying workflows? Is there anything you'd change about the interface? Are there performance, privacy, or technical improvements you'd recommend? I'd especially appreciate criticism from people who actually use developer utilities regularly. Don't hesitate to point out what's bad or unnecessary — that's more useful to me than compliments. If you have a minute, take a look and tell me what you'd change. Thanks! 🙏

2026-08-09 原文 →
AI 资讯

I Built a Crypto-Native Craigslist with Manual Escrow — Here's Why and How

The Problem There are millions of people holding crypto who want to spend it on real things — hire a developer, buy a script, sell design work. But where do they go? Telegram OTC chats → chaotic, no protection, scam-heavy Forum classifieds → threads get buried in hours P2P exchange sections → designed for fiat conversion, not commerce I decided to build a dedicated marketplace for this. What I Built CryptoBoard — a classifieds platform with Web3 wallet authentication. 🔗 https://crypto.my-board.org/ Tech decisions: Auth : Wallet-only (MetaMask, Trust Wallet, WalletConnect). No backend user database with emails and passwords to get hacked. Listings : Icon-based instead of user-uploaded images. Keeps the UI clean and avoids the "flea market" look. Messaging : Built-in chat between buyers and sellers. Escrow : This is the interesting part (see below). The Escrow Problem with Digital Goods Traditional escrow works like this: Buyer sends money to escrow Seller delivers product Buyer confirms → escrow releases money But with digital goods (source code, design files), step 3 is broken: The buyer can receive the files, say "this isn't what I wanted," request a refund, and keep a copy The seller has no recourse The escrow service has no way to verify the claim My Solution: Human-Powered Escrow Instead of just holding funds, the platform admin becomes an active verifier: Seller sends product + testing instructions to admin Admin installs/runs the product on their own machine Admin performs agreed-upon tests and records a screencast Buyer watches the screencast — verified by a neutral party, not the seller If satisfied, buyer sends crypto directly to seller Admin verifies the on-chain transaction Admin delivers files to buyer Admin deletes all copies (per agreement) Is it scalable? Probably not infinitely. But for high-value digital transactions ($100–$10,000+), having a human in the loop is actually a feature, not a bug. Design Philosophy I deliberately chose not to allow user

2026-08-09 原文 →
开发者

Free, Zero-Dependency YouTube Website Embed (Self-Hostable PHP/JS)

Hey DEV community! 👋 If you've ever tried to embed a dynamic YouTube channel feed, a live stream detector, or playlist carousel on a client site, you've probably run into two major issues: Expensive SaaS widgets that slap watermarks on your site unless you pay a monthly fee. Leaking your YouTube API Key directly in the frontend script. To solve this, we built YT Widget —a free, self-hostable, dependency-free JavaScript library that handles YouTube feeds, playlists, channel stats, and live stream status seamlessly. 📦 Where to Get It The project is fully open-source and ready for your production projects: Source Code & Contributions: scott8462 / YT-Widget A free, self-hostable, dependency-free JavaScript library for embedding YouTube feeds, playlists, channel stats, single videos, and live stream status on any website. YT Widget — Free Open-Source YouTube Website Embed A free, self-hostable, dependency-free JavaScript library for embedding YouTube feeds, playlists, channel stats, single videos, and live stream status on any website — just like SociableKIT, but 100% free and open-source. Created and provided free to the developer community by R&S Development . ✨ Features 📺 5 Widget Types feed : Latest channel uploads grid or list live : Auto-detects live broadcasts and embeds the live player — shows a custom Offline Card with recent uploads when offline playlist : Show videos from any YouTube playlist stats : Channel metrics cards (Subscribers, Views, Videos count) single : Responsive single video player with metadata 🔒 Secure PHP Server Proxy ( proxy/ ) : Keep your YouTube API key hidden server-side with built-in CORS, rate limiting, and 5-minute response caching. 🎨 Full Color Customization Light & Dark themes Custom Accent / Button… View on GitHub Alternative Downloads & Mirrors: Download on SourceForge ✨ Core Features 📺 5 Widget Types: Switch layouts instantly ( feed grid/list, live stream detector, playlist fetcher, profile stats , or single responsive video). 🔒 Se

2026-08-09 原文 →
AI 资讯

I Got the Internship Offer… and Then I Had to Say No.

A few days ago, I went for an internship opportunity that I was genuinely excited about. I had been looking forward to it for a long time. When I got the opportunity to attend a 3-day demo/trial period , I went in with a lot of excitement. I wanted to prove myself, learn as much as possible, and hopefully turn those three days into something bigger. And honestly, I gave it my best. I showed up, worked, learned, asked questions, and tried to contribute wherever I could. Then came the moment I had been hoping for. I received the offer letter. ❤️ For a moment, I was extremely happy. After being out of college and working hard to build my skills, finally getting an offer felt like a big step forward. But then I had to look at the practical side. The internship was work from office , and the stipend was ₹7,000/month . The biggest challenge was the distance. I live around 90 km away from the office. When I calculated the daily travel, food, and other expenses, I realized that accepting the internship would put a huge financial burden on me every month. And that was a very difficult realization. Because emotionally, I wanted to say: "Yes, I got an internship. Let's do this!" But practically, I had to say: "I can't afford this right now." So I rejected the offer. And honestly? It hurts. Not because the company did something wrong. Not because I didn't want to work. But because I finally got an opportunity I was excited about, gave it my best during the trial period, received the offer… and still had to walk away from it. I've been feeling pretty bad about it. There is always this thought in the back of my mind: "What if I had just accepted it?" But I'm also trying to remind myself that rejecting one opportunity doesn't mean I've failed. Sometimes an opportunity can be good and still not be right for your current situation. I'm taking this experience as a lesson: Getting an offer is not the final goal. Salary/stipend matters. Location and travel expenses matter. Your time ma

2026-08-08 原文 →
AI 资讯

I built an embeddable screen-time calculator that doesn't phone home

Most embeddable widgets are surveillance with rounded corners. You paste one script tag, it opens a socket back to someone else's server, drops analytics, fingerprints the page, and turns your article into their funnel. I wanted the opposite. I had built a small screen-time calculator for an iPhone side project. You enter daily phone hours, how much of that time you'd actually want back, and your age. It returns the number not just as hours per year, but as waking years of the life you have left . The surprising part was not the maths. The surprising part was that the calculator itself was the first marketing asset I had built that people might reasonably link to. So the next step was obvious: make it embeddable. Constraints I gave myself four rules: No tracking script No backend callback No cookie or storage requirement Useful standalone, but with a real reason to click through That ruled out the normal widget pattern immediately. I did not want a script that asks the host page for DOM access. I did not want the embed to send typed values back to me. And I did not want to bolt analytics onto a tool whose whole public claim is "nothing leaves your device". So the widget became a single static iframe page. The embed snippet This is the whole thing: <iframe src= "https://shantj.github.io/sproutguard/embed.html" width= "100%" height= "620" style= "border:0;max-width:600px" loading= "lazy" title= "Screen time calculator" ></iframe> <p style= "font-size:13px;opacity:.7;margin:6px 0 0" > <a href= "https://shantj.github.io/sproutguard/screen-time-calculator.html?ct=embed-credit" > Screen Time Calculator </a> — free, no signup, runs in your browser. </p> No JavaScript include. No SDK. No npm package. Just an iframe and a credit link. The iframe points at a page that contains the calculator UI and the arithmetic. Because it is a static page, the host site never has to trust my script with its DOM. The actual calculator logic The core number is intentionally boring: const LIF

2026-08-08 原文 →
AI 资讯

Building a Chrome Extension to Auto-Save Gemini Chat Logs using AI (Part 1)

This article was originally published on e-shikumi-labo . Hello, I'm Shin from e-Shikumi-Labo. How do you all manage your conversations with Gemini? When you manage to extract a useful response from the AI, have you ever thought, "I want to keep this somewhere"? It all started from a simple, practical desire in my daily work: "I want to automatically save useful conversations from Gemini to a spreadsheet before they fade away." So, borrowing the power of Generative AI (Gemini), I tried making my own personal Chrome extension. Over this four-part series, I will write about "systematized thinking"—the process of utilizing AI to build tools and independently maintaining them. In Part 1, I'll share the developmental dialogue process: "How did I instruct the AI, what information did I provide, and how did we complete the prototype?" 1. A Prompt That Says: "Don't Guess, Ask for the Information You Need" As the very first step in development, I threw this prompt directly at Gemini itself. "I want to save Gemini's responses to a spreadsheet using a Chrome extension. Tell me how to build it without using your imagination. If you need any specific information, please point it out." The key here lies in two constraints: "without using your imagination" and "point out if you need information." When you try to build a web data extraction tool using AI, the AI often tends to "guess" the internal structure of the webpage (like HTML tags and class names) on its own and write the code. And even when you test this supposedly completed code, you fall into the trap of it not working because it doesn't align with the actual screen structure. To avoid this trap, I explicitly communicated, "Don't guess on your own. If there's missing information, I want you to demand it from the human side." 2. A Game of Catch with AI Using DevTools When I threw this prompt, the AI returned the following response: AI: "Understood. To create code that works reliably while eliminating guesswork, please retr

2026-08-08 原文 →
AI 资讯

I Built The Most Advanced Job Application Tracker

If you're actively applying for jobs, you probably know the struggle: Did I already apply to this company? Which resume version did I send? What salary did I mention when I applied? What was the budget mentioned in the job posting? When did I apply for this one? Which interviews are scheduled this week? What were the HR contact details again? What exactly were the requirements for this role? When is my next interview? Where did I even find this posting? How many of my applications are actually turning into interviews? Every one of those is answerable. The problem is that the answers are scattered across a spreadsheet, a notes app, your inbox, and your memory — and reassembling them takes longer than the follow-up you were trying to send. Spreadsheets are where most people start, and they hold up until somewhere around application number twelve. After that, searching, filtering, and keeping the thing current becomes its own small job — and a spreadsheet still won't tell you that six applications have been sitting in "Applied" for a month, or whether your last twenty went better than the twenty before them. That's why I built HireLoop — an advanced job application tracker meant to reduce the mental load of a job search rather than add to it. Live app: hireloop.yogeshchavan.dev — free to use, with a demo account if you'd rather look around before signing in. Check out the application demo video below: Check out some preview images of the application The short version With HireLoop you can: Track every application in one place — status, dates, salary, source, and links See where your search stands at a glance on a dashboard Move applications through a Kanban pipeline Search, filter, and sort as the list grows See interviews and deadlines on a calendar Analyse interview rates, offer rates, application trends, and which sources actually work Store notes, resume versions, HR contacts, salary details, and job links per application Mark the ones that matter as favourites Kee

2026-08-08 原文 →
AI 资讯

A Practical Guide to Converting Inches, Centimeters, Meters, Feet and Millimeters

If you work with measurements often enough, you eventually run into the same problem: the value you have isn't in the unit you need. A product specification might be in inches. A construction drawing might use feet. A European supplier might give you dimensions in centimeters or millimeters. The actual formulas are usually simple. Finding the right conversion, avoiding rounding mistakes, and checking a large list of values can be more annoying than the math itself. Here are the conversions I use most often and a few practical ways to work with them. Inches to centimeters The basic relationship is: 1 inch = 2.54 centimeters So the formula is: centimeters = inches × 2.54 For example: 10 inches × 2.54 = 25.4 cm This is probably the most common conversion when moving between imperial and metric measurements. If you just need to check a value quickly, Pulgadas a CM has an interactive converter along with a conversion table and frequently asked questions. Centimeters to inches Going in the opposite direction means dividing by 2.54: inches = centimeters ÷ 2.54 For example: 25.4 cm ÷ 2.54 = 10 inches You can use the CM a Pulgadas converter when you need to work in this direction. This is particularly useful when a measurement is provided in centimeters but the product, tool, or specification you're working with uses inches. Meters to inches Meters are larger units, so the conversion factor is correspondingly larger. One meter contains approximately: 39.3700787 inches Therefore: inches = meters × 39.3700787 For example: 2 meters ≈ 78.7401574 inches For a quick calculation, you can use the Metros a Pulgadas converter . This conversion can come up when working with room dimensions, furniture measurements, fabric, sports equipment, or other products where metric and imperial specifications are mixed. Inches to meters The reverse calculation is: meters = inches × 0.0254 For example: 100 inches × 0.0254 = 2.54 meters The Pulgadas a Metros converter is useful when an imperial meas

2026-08-08 原文 →
AI 资讯

Spring Boot For Beginner

🚀 Building a REST API with Java Spring Boot: A Practical Beginner’s Guide If you're coming from Java and want to move into backend development, Spring Boot is one of the best frameworks to learn. It removes a lot of the boilerplate traditionally associated with Spring and makes it surprisingly easy to build production-ready REST APIs. In this article, we'll build a simple Blog REST API using: ☕ Java 🌱 Spring Boot 🌐 Spring Web 🗄️ Spring Data JPA 🐘 PostgreSQL 📦 Maven 🧪 Postman By the end, we'll have an API that can: Create a blog post Get all blog posts Get a post by ID Update a post Delete a post 1. What is Spring Boot? Spring Boot is a framework built on top of the Spring Framework that makes it easier to create Java applications. Without Spring Boot, you often need to configure many things manually. Spring Boot gives us: Auto-configuration Embedded servers Starter dependencies Production-ready features Easy REST API development A simple Spring Boot application can be started with: @SpringBootApplication public class BlogApplication { public static void main ( String [] args ) { SpringApplication . run ( BlogApplication . class , args ); } } That's enough to start our application. 2. Create the Spring Boot Project The easiest way to create a Spring Boot project is through Spring Initializr . Choose: Project: Maven Language: Java Spring Boot: Latest stable version Packaging: Jar Java: 17+ Add these dependencies: Spring Web Spring Data JPA PostgreSQL Driver Validation Lombok Your project structure will look something like: src └── main └── java └── com.example.blog ├── BlogApplication.java ├── controller ├── service ├── repository ├── entity └── dto This separation will become important as our application grows. 3. Create the Blog Entity Let's create a simple BlogPost entity. @Entity @Table ( name = "blog_posts" ) public class BlogPost { @Id @GeneratedValue ( strategy = GenerationType . IDENTITY ) private Long id ; @NotBlank private String title ; @NotBlank @Column (

2026-08-08 原文 →
AI 资讯

Java Spring Boot Logging: Log Levels, Logback, JSON Logs & Production Best Practices

Production-Grade Logging in Spring Boot: A Complete Guide to Logging Levels, Files, JSON Logs, Correlation IDs, and Best Practices Logging is one of the most important parts of a production backend system. When everything works, you may not think much about logs. But when production starts returning 500 errors at 2 AM, a customer reports that an API is failing, or a payment request behaves unexpectedly, logs become one of your most important debugging tools. Poor logging makes production debugging painful. Good logging helps you answer: What happened? When did it happen? Which user triggered it? Which request caused it? Which service handled it? How long did it take? What failed? Why did it fail? What should we investigate next? In this article, we will build a production-grade logging strategy for a Java Spring Boot application . 1. What Does Production-Grade Logging Mean? Production-grade logging is not simply: log . info ( "User created" ); Production logging should be: Structured Searchable Consistent Secure Configurable Environment-aware Correlated across requests Useful during debugging Suitable for monitoring and alerting A good logging architecture might look like this: Spring Boot Application | v Logback | +---- application.log | +---- error.log | +---- audit.log | +---- access.log | v Log Aggregation | +---- ELK +---- Grafana Loki +---- CloudWatch +---- Datadog +---- Splunk The goal is not to log everything. The goal is to log the right information at the right level . 2. Understanding Log Levels Spring Boot uses SLF4J as the logging abstraction and commonly uses Logback as the underlying logging implementation. The most common log levels are: TRACE DEBUG INFO WARN ERROR The order represents increasing severity. TRACE TRACE is the most detailed logging level. Example: log . trace ( "Entering calculateInvoice() with customerId={}" , customerId ); Use TRACE for very detailed diagnostic information. Usually: Production: OFF Development: Sometimes ON Debugging

2026-08-08 原文 →
AI 资讯

Building a Leak-Safe gRPC Frame Decoder on Reactor Netty

This is the second article in my grpc-reactor series. The first article explains why I chose to build the runtime directly on Reactor Netty and where its compatibility boundary sits. This article moves one layer down into the Stage 1 protocol implementation: the frame decoder that every RPC shape relies on. gRPC protobuf messages are not written directly as raw bytes into HTTP/2 DATA frames. Every message starts with a five-byte envelope: byte 0 bit 0 indicates compression; bits 1-7 must be zero bytes 1-4 unsigned big-endian payload length byte 5..n protobuf message, or its compressed representation Encoding this envelope is straightforward. The difficult part is decoding it without assuming that one input buffer contains one complete frame. HTTP/2, TCP, and Reactor Netty do not promise that buffer boundaries will line up with gRPC message boundaries. This post describes the Stage 1 protocol layer. The project has since progressed beyond it, but the ownership and bounded-decoding rules introduced here remain the foundation for the later transport stages. Encoding Must Define Ownership The contract of GrpcFrameCodec.encode is deliberately explicit: the returned frame and the input message have independent lifetimes. Encoding must not move the input reader index or release the input buffer. The implementation currently copies the readable bytes into a byte array before applying compression: public static ByteBuf encode ( ByteBufAllocator allocator , ByteBuf message , GrpcCompression . Codec compression ) { boolean compressed = ! compression . name (). equals ( "identity" ); byte [] payload = new byte [ message . readableBytes ()]; message . getBytes ( message . readerIndex (), payload ); if ( compressed ) { payload = compression . compress ( payload ); } return allocator . buffer ( GrpcFrameCodec . HEADER_SIZE + payload . length ) . writeByte ( compressed ? 1 : 0 ) . writeInt ( payload . length ) . writeBytes ( payload ); } This is not a zero-copy implementation, and

2026-08-08 原文 →
AI 资讯

I Tried Building JavaScript Games Without a Game Engine. Here's What I Learned

I am a digital marketer, not a professional developer or game developer. Most of my career has been focused on SEO, growth marketing, paid acquisition, content, and digital strategy. When I started building GamesMom, however, I found myself learning much more about web development than I expected. GamesMom is a collection of free educational games and learning activities for kids that run directly in the browser. The site includes math games, word games, typing games, memory games, puzzle games, classroom games, quizzes, and other interactive activities. The idea was simple: make games that children can open and play without downloading an application or creating an account. I initially assumed that building browser games would require a dedicated game engine or a large JavaScript framework. After experimenting with different approaches, I found that many of the games I wanted to create could be built with ordinary HTML, CSS, and JavaScript. That was probably the most useful lesson I learned from the project. You don't always need a complicated technology stack to create an interactive web experience. I Started With the Simplest Approach When you're not a professional developer, it is tempting to look for the most sophisticated solution available. I did this too. I spent time looking at frameworks, game engines, libraries, and different ways of structuring interactive applications. Eventually I started asking a much simpler question: what does this particular game actually need? A basic educational game might need to display a question, accept an answer, update a score, show feedback, and move to the next question. Another might need a timer, a few buttons, and some randomization. Those requirements don't automatically justify a game engine. For simple browser games, the browser already provides a lot of what you need. HTML, CSS and JavaScript Can Go a Long Way The basic combination is surprisingly capable. HTML provides the structure of the page. CSS controls the v

2026-08-08 原文 →