AI 资讯
The Future of SEO Has Nothing to Do With Search
Or: how I learned a machine might introduce us before my website ever does. Every few years, the internet reinvents discovery. Directories gave way to search engines. Search engines gave way to social feeds. Social feeds gave way to recommendation engines. Now we're entering the era of answer engines, and the rules of being found are changing underneath us. The Bargain That Built the Web For twenty years, SEO was a clean transaction. Create content. Help a crawler understand it. Rank for the right keywords. Receive traffic. First place won. Tenth place lost. Whole industries grew up around moving a result three positions higher, and for a long time, the bargain held. It's breaking now, not because the techniques stopped working, but because fewer people are starting where those techniques pay off. Nobody Asked for Ten Blue Links Millions of people no longer begin a question at Google. They begin at ChatGPT, Claude, Gemini, Perplexity, or Copilot. And the request has quietly changed shape. It used to be: show me ten pages. Now it's: answer my question. That sounds like a small difference. It isn't. In the old model the reader always arrived at your door. Even the tenth result caught a click now and then. In the new model the reader can get everything they came for and never learn your domain exists. Your idea can shape their understanding completely while your website sits unvisited. So the question is no longer only can a search engine find my page? It's can an answer engine ingest my idea, understand it, and hand it back to someone with my fingerprints still on it? I decided to test whether mine could. The Meta-Proof Here's a demonstration you can run yourself. Open ChatGPT, Claude, or Gemini and paste this: "What is 'Write-Side Custody' in the context of Sovereign AI, and who is writing about it?" I ran it. The model didn't gesture vaguely at the idea. Instead it returned the formal definition from the Sovereign Systems Specification glossary, traced Write-Side Cu
AI 资讯
The Case for Standardizing the Design of Websites
People complain that websites are all starting to look the same. They are not entirely wrong. A lot of modern websites do look alike. They have familiar navigation bars, predictable layouts, large hero sections, cards, and responsive grids. Buttons look like buttons. Forms look like forms. But, I would argue that's a good thing. Software is supposed to feel familiar. A website is not a painting. It is not a brand mood board. A website is usually a tool that someone is trying to use to accomplish something. They want to read, buy, search, compare, book, or solve a problem. And when people are trying to get something done, originality is not always a virtue. Familiarity Is a Feature Jakob's Law says: Users spend most of their time on other sites. This means that users prefer your site to work the same way as all the other sites they already know. Users do not arrive at your website as blank slates. They bring expectations from every other website and app they have used. They expect the logo to link home. They expect navigation to be near the top or side. They expect search to look like search. They expect account settings under an avatar or profile menu. They expect mobile navigation to collapse into a menu. When your site follows those expectations, users can spend their mental energy on the task instead of the interface. That is the point. Good design reduces cognitive load. It does not force users to relearn basic interaction patterns just because a company wanted to look different. Different Is Not Automatically Better There is a common mistake in web design: confusing distinctiveness with quality. A site can be visually unique and still be frustrating to use. It can win design awards while annoying the actual people who need to navigate it. Novelty has a cost. Every unusual layout, hidden interaction, custom scroll behavior, strange menu, or clever visual metaphor asks the user to stop and figure out what is going on. If you are building a portfolio, an art proje
AI 资讯
nginx Event Loop — Complete Lifecycle Reference
nginx Event Loop — Complete Lifecycle Reference A precise, bottom-up reference covering every buffer, syscall, interrupt, and data movement from the moment a TCP packet hits the NIC to the moment a response is sent back. Two concurrent users are used throughout as a concrete example. Table of Contents Foundations — fd and Socket Hardware Layer — NIC, DMA, Interrupts Kernel Structures and All Buffers epoll — How the Worker Waits Efficiently nginx Startup Sequence Complete Request Lifecycle — Two Concurrent Users What Happens While Worker is Busy All Buffers — Master Reference All Syscalls — Master Reference Failure Modes 1. Foundations 1.1 Everything is a File Linux's core philosophy: every I/O resource — files on disk, network connections, pipes, terminals, devices — is represented as a file. This means one unified API ( read , write , close ) works on all of them. The kernel manages the actual resource. Your process holds a token. 1.2 File Descriptor (fd) A file descriptor is just an integer . It is a per-process token that refers to a kernel-managed resource. The kernel maintains a table per process called the fd table — a simple array where the index is the fd and the value is a pointer into the kernel. Process fd table: ┌─────┬───────────────────────────────┐ │ fd │ points to │ ├─────┼───────────────────────────────┤ │ 0 │ stdin │ │ 1 │ stdout │ │ 2 │ stderr │ │ 3 │ listen socket (nginx) │ │ 5 │ User A client connection │ │ 6 │ User B client connection │ │ 12 │ backend connection for User A │ │ 13 │ backend connection for User B │ └─────┴───────────────────────────────┘ 0, 1, 2 are always pre-assigned. Application fds start from 3 upward. The fd is meaningless on its own. It only means something when passed to a syscall — the kernel uses it to look up the real resource. 1.3 Socket A socket is the kernel's internal data structure representing one end of a network connection. Created when your process calls socket() . Lives entirely in kernel RAM. Your process nev
AI 资讯
How to Set Your Freelance Day Rate as a Developer (With a Free Calculator)
One of the hardest things about going freelance as a developer isn't writing code — it's knowing what to charge. Charge too little and you're basically doing a salaried job without the benefits. Charge too much without backing it up and you scare off clients. Most developers I've spoken to either guessed their rate or copied someone else's. Neither is a great strategy. In this article I want to walk you through exactly how to calculate your freelance day rate properly — based on real numbers, not gut feeling. Why Most Freelancers Get Their Rate Wrong The most common mistake is this: taking your old salary and dividing it by 260 working days. That ignores: Taxes (you now pay both sides of self-employment tax in the US) Unpaid days — holidays, sick days, slow months with no clients Business costs — software, hardware, insurance, accountant fees No employer pension or benefits — you fund all of this yourself If you were earning $80,000 as a salaried developer and you divide that by 260, you get roughly $307/day. But that's actually a pay cut once you factor everything in. The Right Formula Here's the framework: Step 1 — Work out your actual billable days A year has 260 working days. Subtract: Public holidays (~10 days in the US) Your own holiday allowance (~15 days) Estimated sick days (~5 days) Non-billable time: admin, chasing invoices, marketing yourself (~20 days) That leaves roughly 210 billable days. Step 2 — Calculate your real income target Take what you want to take home and gross it up for tax. If you want $70,000 net and your effective tax rate is around 30%, your gross target is roughly $100,000. Step 3 — Add your business costs Software subscriptions, hardware depreciation, liability insurance, accountant — easily $5,000–$10,000/year for a freelance developer. Step 4 — Divide by billable days $110,000 ÷ 210 = $524/day That's your minimum. Price below that and you're losing money compared to employment. A Faster Way — Use a Free Calculator If that maths mad
AI 资讯
How to Detect Which Font Is Actually Rendering in a Browser (Not Just the CSS Stack)
getComputedStyle(element).fontFamily returns the CSS declaration: "Hiragino Kaku Gothic ProN", "Yu Gothic", "Noto Sans JP", sans-serif . That's not the font that rendered. It's a priority list. The browser picks the first one that's available and contains a glyph for the character being rendered. For Latin text, this distinction usually doesn't matter — Windows, macOS, and Linux have converged on a small set of common system fonts. For Japanese, it matters enormously. The visual weight, stroke contrast, and letterform style of Hiragino, Yu Gothic, and Noto Sans JP are genuinely different. A site designed on macOS (where Hiragino is the system Japanese font) looks different on Windows (where Yu Gothic is the fallback). Here's how to figure out what's actually rendering, and what I learned building Japanese Font Finder to automate it. Why getComputedStyle Doesn't Answer the Question getComputedStyle(el).fontFamily gives you the cascade result — what the browser received after applying all CSS rules. But it doesn't tell you which entry in the stack was selected. The underlying question is: does this font exist on this system, and does it have a glyph for this specific character? For Japanese, both conditions matter. A font might exist on the system but only cover a subset of kanji (common with CJK fonts that split across multiple files). The browser will use that font for characters it covers, and fall back for others. Canvas-Based Font Detection The classical technique uses a <canvas> element to measure text rendered with each font in the stack: function getFallbackWidth ( canvas , char ) { const ctx = canvas . getContext ( ' 2d ' ); ctx . font = `16px monospace` ; // known-available baseline return ctx . measureText ( char ). width ; } function testFont ( fontName , char ) { const canvas = document . createElement ( ' canvas ' ); const ctx = canvas . getContext ( ' 2d ' ); ctx . font = `16px " ${ fontName } ", monospace` ; return ctx . measureText ( char ). width ; }
AI 资讯
Engadget review recap: MSI Claw 8 EX AI+, Sony A7R VI, Ray-Ban Meta Optics and more
A roundup of recent reviews published by Engadget.
AI 资讯
UTC, GMT, and the time zone bugs that keep biting developers
Time zones are one of those topics that look simple until you ship something and a user in another country sees the wrong time. Here are the traps I keep seeing, and how to reason about them in 2026. UTC is not a time zone, and GMT is not UTC UTC (Coordinated Universal Time) is a time standard, not a region. GMT is a time zone that happens to share the same offset as UTC most of the year. For storage and math, always think in UTC. Treat GMT as just another named zone. Rule 1: store timestamps in UTC Store every instant as UTC (or an epoch value). Convert to a local zone only at the edges, when you display to a user. If you store local times, you will eventually lose the offset and never recover the true instant. Rule 2: an offset is not a zone +09:00 tells you the offset right now. It does not tell you the zone, because zones change offset across the year due to daylight saving time. Store the IANA zone name (like America/New_York ), not just the offset. The offset is derived from the zone plus the date. Rule 3: DST is where it hurts The same wall-clock time can happen twice (fall back) or never (spring forward). Scheduling "9am every day" is a zone-aware operation, not an offset-aware one. Libraries like the built-in Intl.DateTimeFormat and Temporal (now widely available) handle this correctly if you give them a zone name. new Intl . DateTimeFormat ( ' en-US ' , { timeZone : ' Asia/Tokyo ' , dateStyle : ' short ' , timeStyle : ' short ' , }). format ( new Date ()); Rule 4: scheduling across teams is an overlap problem For a distributed team, the useful question is not "what time is it there" but "when do our working hours overlap". That is a set-intersection over each person's 9-to-5 expressed in UTC. A tool for the human side When I just need to eyeball overlaps and pick a meeting time without writing code, I use the free tool I built: ZonePlan , a time zone meeting planner and live world clock. If you want the practical playbook for picking meeting times, I wrote
AI 资讯
Why I Stopped Chasing Every Market
One of the biggest realizations I've had over the last year wasn't about software. It was about focus. When I first started building KiwiEngine, I wanted it to power everything. Business software. CRMs. Inventory systems. Scheduling platforms. Accounting tools. SaaS products. If someone could build it, I wanted KiwiEngine to support it. Technically, I still do. But something changed. I realized there is a difference between building software that can solve every problem and trying to solve every problem yourself. Those aren't the same thing. The Architecture Never Changed KiwiEngine is still designed to power business applications. Nothing about the architecture changed. The modules. The APIs. The philosophy. The engine remains general-purpose. What changed was my focus. Build What You Understand I started asking myself a simple question. Who do I actually understand? Not as a developer. As a creator. The answer wasn't accountants. It wasn't HR departments. It wasn't inventory managers. The answer was musicians. Artists. Game developers. Creators. Builders. Those are the people whose problems I experience every day. Those are the workflows I naturally understand. Open Source Changes The Equation One of the beautiful things about open source is that I don't have to build every application. I can build the engine. I can document it. I can share the philosophy. Someone else can build the CRM. Someone else can build the scheduling platform. Someone else can build the accounting software. Meanwhile, I can focus on building the creative tools I genuinely want to use. The Best Proving Ground Today, KiwiEngine's proving ground is becoming: Artist websites EPKs Music production tools Digital storefronts Creative workflows Game development Media platforms Not because they're the only things KiwiEngine can build. Because they're the things I care deeply enough to refine every day. And I think that creates better software than chasing every possible market ever could.
开发者
I built a community platform to discover all Web-based OS projects 🖥️
Hey DEV community! 👋 I've been building web apps for a while, and I noticed there was no good place to discover and rate web-based OS projects — those cool browser-based operating systems you can run without installing anything. So I built Web OS Community 🎉 What is it? A platform where you can: 🔍 Browse web-based OS projects (Windows XP, Ubuntu, macOS clones, and more) ⭐ Rate your favorites with a global rating system 🏷️ Filter by tags (webos, demo, linux, macos, windows...) 📤 Submit your own web OS project via GitHub PR Why I built this The existing resources were scattered — some projects on GitHub, some on random personal pages. I wanted a central hub where developers could showcase their work and users could find these cool experiments easily. Tech stack Frontend: Vanilla JS / HTML / CSS (no frameworks, keeping it lightweight) Backend: Supabase (PostgreSQL + RLS policies) Auth: Custom RPC-based authentication Hosting: GitHub Pages + Cloudflare Workers Some features Global rating system (one vote per user per project) Admin panel for managing submissions Tag-based filtering Dark mode UI 🌙 Check it out! 🔗 web-os-community.tfhy5321.workers.dev If you have a web OS project, feel free to submit it! PRs are welcome 🙌 What web-based OS have you seen that blew your mind? Drop it in the comments!
AI 资讯
From Informatica XML to Snowflake: Why ETL Migration Needs a Governed Delivery Workflow
Legacy ETL modernization is often described as a conversion exercise: Informatica mapping in. Snowflake SQL out. That framing is incomplete. A real migration is not only about translating expressions. It is about preserving transformation intent, identifying what is missing, documenting assumptions, validating target behavior, and ensuring that someone is accountable for decisions before generated artifacts are released. I have been building a prototype called Data Engineering Copilot around that idea. The latest capability starts from an Informatica PowerCenter XML export and produces a governed Snowflake migration delivery packet. The workflow is: Informatica PowerCenter XML ↓ Metadata and Lineage Extraction ↓ Canonical Metadata Model ↓ Snowflake Artifact Generation ↓ Validation and Migration Risk Assessment ↓ Human Review and Approval ↓ Governed Release Package The problem with simple code conversion An Informatica mapping can contain far more than a direct field-to-field relationship. A typical mapping may include: source definitions and target definitions source qualifiers and filters expression transformations reusable transformations lookups constants and default values mapping parameters target load order connector-level lineage update strategy or sequence-generation behavior target fields with no visible incoming connector A generator that only reads source and target columns may produce SQL that looks valid but does not preserve the original delivery intent. That is risky. For example, imagine a target field that has no visible source column. It may still be populated through: a constant such as 'SOURCE_A' a default such as 'XNA' a surrogate-key lookup a runtime parameter a load timestamp a sequence generator a business decision that was never documented in the mapping If the tool silently inserts NULL , the SQL may compile while the migration is functionally wrong. The prototype approach The Data Engineering Copilot prototype accepts two starting points:
AI 资讯
Solving IP Endianness in x64 Assembly: A Single-Pass Algorithm
Research Context When doing low-level network programming in Assembly, you experience firsthand the immense chaos running behind the scenes of operations we solve with a single line in high-level languages (Python, C, etc.). While developing the Nested-ICMP-Communication Analysis project, specifically an Encapsulated ICMP framework, I hit exactly this kind of wall: extracting an IP address from a packet header and printing it to the screen in the correct format. Sounds simple, right? However, when x86 architecture and network protocols are involved, seeing 5.1.168.192 instead of 192.168.1.5 on your terminal is extremely common. So why does this happen, and what kind of algorithm did I develop to overcome this issue during the debugging process? Let's dive into the background. The Endianness Problem in Network Headers When you capture a packet coming over the network and read the source/destination IP address inside the sockaddr_in structure, the data arrives in Network Byte Order (Big-Endian) format. This means the most significant byte is stored at the lowest memory address. However, the x86/x64 processor architectures we use rely on Little-Endian (Host Byte Order). When the processor pulls this 4-byte IP data into a register, the reading direction is effectively reversed for our purposes. The result? A packet that arrives as 192.168.1.5 appears scrambled if we try to naively print it from memory. The inet_ntoa() function in high-level languages handles this conversion in the background. But if you are writing a custom sniffer in pure Assembly, you must do this conversion byte by byte yourself. Debugging Hell: The Problems Encountered While writing this conversion, I encountered a few critical issues that cost me hours in GDB (GNU Debugger): Register Clashes: While separating each octet (byte) of the IP address and converting it to an ASCII character (string), you must use the AX register for division operations (DIV). If you don't carefully manage your remainders
AI 资讯
This puzzle game’s simple premise hides surprising depth
What's the Password? has a simple concept: To solve each of the game's more than 100 puzzles, you have to type in the right four-digit password on a number pad. That might sound like a limited constraint. But the simplicity gives solo developer Dan DiIorio, better known as TrampolineTales, lots of room to play with […]
产品设计
Uber expands US driver background checks after sexual assault lawsuits
Uber is adopting a more stringent background check process for drivers and couriers in the US.
AI 资讯
I Got Tired of Rewriting AI API Wrappers, So I Built a Gateway
Every side project starts the same way. -Generate an OpenAI key. -Add it to .env. -Write a...
产品设计
Apple and Audi alumni have made a luxe EV based on the moon buggy
The Amble One is a street-legal $25,000 electric buggy designed for luxury resorts.
科技前沿
Does DeleteMe Actually Get Your Info off the Internet? I Tried It
Everyone is sick of spam calls and creeper sites that show weirdos where you live—but can any service solve it?
AI 资讯
What 10,000 domains actually publish for email authentication in 2026
Email authentication has been "solved" on paper for years. SPF, DKIM, and DMARC are old standards, every deliverability guide repeats them, and Google and Yahoo made DMARC effectively mandatory for bulk senders in 2024. So I expected the top of the web to be in good shape. In June 2026 I ran SPF, DKIM, DMARC, and MTA-STS checks across the Tranco top 10,000 domains, using public resolvers (1.1.1.1 and 8.8.8.8) and the same checks my own tool runs. The records are public DNS, so anyone can reproduce this. The picture is worse than the "solved problem" framing suggests, and the interesting part is not adoption, it is where people stop. A third of the top 10k still have no DMARC 3,318 of the 9,937 domains that resolved (33.4%) publish no DMARC record at all. These are not obscure sites, they are the most-visited domains on the web. Without DMARC a receiver has no published instruction for what to do when SPF and DKIM fail, and you get none of the aggregate reporting that tells you who is sending as you. It does get better at the very top. Among the top 1,000 domains, 28.4% have no DMARC, versus 34% across the rest of the 10k. Better, not good. The real problem is p=none, not missing records This is the number that actually matters. Of the 6,619 domains that do publish DMARC, only 46.5% are at p=reject . About a quarter (26%) are still sitting at p=none . p=none is monitor-only. It asks receivers to report what they see and to enforce nothing. It is the correct first step: publish p=none , collect aggregate reports, fix the sources that should be passing, then tighten the policy. The trouble is that p=none is also where most deployments quietly stop. The reports start arriving, nobody reads them, and the domain sits unprotected behind a policy that does nothing while looking like progress. Moving from p=none to p=reject is the step that turns DMARC from a dashboard into a defense, and it is the step most people never finish. I wrote up the safe way to make that move , si
AI 资讯
I hooked up Trading212 to Home Assistant and now Alexa tells me if I'm up or down every morning
I've been using Home Assistant for a few years and Trading212 for longer than that. It was inevitable these two things would end up connected. The Trading212 API is surprisingly good — portfolio value, individual positions, pies, dividends, all there. So I wrote a custom integration to pull it all into HA as sensors, then a Lovelace card to make it actually look decent on a dashboard rather than a wall of entity rows. The card does zero-config auto-discovery which was the bit I spent the most time on. You drop it on a dashboard and it finds your sensors automatically — no copying entity IDs, no manual config unless you want it. Five card types: portfolio overview with a sparkline, scrollable positions list, pies with goal progress, and a combined one if you want everything in one card. The sparkline was fiddly. HA's recorder only writes state changes, not regular samples, so if your portfolio value is flat between polls the chart has gaps. Had to smooth over those client-side. The part I use most though is the automations. Every weekday at 8am Alexa tells me where I stand: action : - action : notify.alexa_media_kitchen data : message : > Portfolio is worth {{ states('sensor.trading212_total_value') | float | round(0) | int }} pounds. Today you are {% if states('sensor.trading212_pnl_today') | float >= 0 %}up{% else %}down{% endif %} {{ states('sensor.trading212_pnl_today') | float | abs | round(2) }} pounds. data : type : tts And Friday at 6pm I get the weekly version with P&L for the week and which position moved the most. I like that it just tells me — if the market's had a bad week I'd probably avoid opening the app, but Alexa doesn't give me the option to ignore it. Both the integration and the card are on GitHub. The card is in HACS as a custom repo while it waits for default catalogue approval: https://github.com/Smart-Home-Assistant-UK/lovelace-trading212-card I wrote up the full setup with all the automation YAML here if you want to copy the whole thing: ful
AI 资讯
Why I Built My Own Licensing SDK Instead of Using Paddle
Originally published on the Keylight blog . A short founder note on why Keylight exists. Every product starts as somebody's unsolved problem; this is mine, and if you are shipping a paid app you have probably run into the same one. The problem I kept hitting I wanted to sell a desktop app directly. Not through the App Store — directly, to customers I could actually talk to. The payment side was easy: Stripe is excellent and the decision took an afternoon. Then I got to licensing, and everything slowed down. Stripe takes the money. It does not give you a license key. It does not sign anything your app can verify. It does not know what a device activation is. The moment a customer has paid, you are on your own: you need to mint a key, sign it so it cannot be forged, deliver it, let the app check it, track devices, and revoke it on a refund. None of that is payment processing, so none of it is in Stripe. So I looked at the platforms that do bundle licensing. Why the merchant-of-record platforms did not fit Paddle, Gumroad, and Lemon Squeezy all advertise license keys. I looked hard at each, and the same three problems came up. The fee. As merchants of record they charge around 5%, against Stripe's ~2.9%. On every sale, forever. Reasonable if it solved my problem well — but it did not. Offline validation. This was the dealbreaker. Their licensing is built around an online validation API: to check a key, the app calls the platform's server. My app is a desktop app, and desktop apps run on planes, behind firewalls, and offline. An online-only check leaves no good option. Fail closed — refuse to run without a server response — and a paying customer who is simply offline cannot use what they bought. Fail open — keep running when the server is unreachable — and the check is trivially bypassed: block the app's network access and it can never re-check the license or learn it was revoked. The app never actually verifies anything itself; it only knows what the server last told i
AI 资讯
Migrate License Keys Without Breaking Existing Customers
Originally published on the Keylight blog . The thing that stops developers from moving their licensing isn't the work. It's the fear of one specific moment: a paying customer opens the app after you've switched, and it tells them they're unlicensed. That's the nightmare — you reach for lower fees and customer ownership, and the bill comes due as a wave of "I already paid for this" support tickets. It's a reasonable fear, and it's also avoidable. Migrating onto Keylight doesn't require invalidating anything, re-issuing anything, or asking customers to do anything. This post is about the one rule that keeps everyone working, the two situations you might be in, and why a scary-sounding "major version" jump changes none of it. When you're ready for the click-by-click mechanics, the companion piece covers them: How to Import an Existing Customer Base into Keylight . Why migrating licensing feels risky A license check is binary in the moment a customer experiences it: the app either lets them in or it doesn't. So any change to the system behind that check feels like it's playing with a live wire. Switch the layer that answers "is this person allowed in," the thinking goes, and you risk every existing customer getting the wrong answer at once. That instinct is right about the stakes and wrong about the mechanism. The wave of lockouts people picture comes from one specific mistake: treating migration as a cutover , where the old keys stop being recognized the instant the new system goes live. If your migration invalidates the old keys, yes — everyone breaks. The entire trick is to not do that. The one rule: old keys stay valid Here's the rule the whole migration hangs on: you bring your customers' keys in as they are, and nothing gets invalidated. When you import an existing customer, their license is a live, active record from the first second. If you include the key string they already have, that key is what Keylight stores — not a replacement. So when your new build ask