AI 资讯
How I built a Minecraft server list that ranks by real player votes (not bots)
Hi, I'm Hugo. I built MinecraftServers-List.com — a Minecraft server directory that ranks servers by genuine player votes and uptime. Why I built it Most existing Minecraft server lists have the same problem: the rankings are easily gamed. Server owners run scripts to inflate their vote counts, and players searching for a good server end up with a list that reflects who has the best bots, not which servers are actually worth playing on. I wanted to fix that. What makes it different Vote integrity — votes are tied to real player sessions and IP validation, making bot voting significantly harder Uptime monitoring — servers that go offline lose ranking visibility automatically Player reviews — verified players can leave reviews with star ratings, giving prospective players real signal Java & Bedrock — both editions listed and filterable by gamemode, version, and country The tech stack Built with TanStack Start (React SSR), Supabase for the database, and deployed on Cloudflare Workers. The SSR approach was important for SEO — server listing pages need to be fully rendered for Googlebot to index individual server pages properly. What I've learned so far Getting a new directory site indexed by Google is genuinely hard. The challenge isn't technical — it's convincing Google that hundreds of server listing pages are individually worth indexing when they all share a similar template structure. The solution has been enriching each server page with structured data (VideoGame schema with AggregateRating), genuine user reviews, and making sure every page has a meaningfully unique meta description generated from real server data — version, gamemode, player count, country. Still a work in progress but the site is live, servers are actively listed, and players are voting daily. Try it If you run a Minecraft server, you can list it free at https://minecraftservers-list.com If you're looking for a server to join, the SMP list and survival list are good starting points. Happy to answe
AI 资讯
Your structured data is probably broken, and your crawler isn't telling you
Most on-page audits catch the obvious stuff: a missing title here, a duplicate meta description there. The thing that quietly costs you rich results is structured data that exists but is invalid, and most flat-list crawlers either skip it or bury it. Here is why it happens and how to catch it. The problem, concretely You add FAQ schema to a product page to win that expandable rich result in Google. You paste a JSON-LD block into the head, ship it, and move on. Six weeks later the rich result never showed up, and nobody knows why. The usual culprits are small and silent: A @type that does not match the content (FAQPage with no mainEntity ). A required property missing ( acceptedAnswer without text ). A trailing comma or a stray character that makes the JSON parse fail entirely. Schema that contradicts what is actually on the page, which Google can flag as spammy and ignore. None of these throw a visible error. The page renders fine. The schema is just dead weight, and a standard "issues" crawl that only counts titles and headings walks right past it. How to catch it First, validate the JSON itself. A block that does not parse is invisible to search engines. Even a quick local check surfaces the dumb-but-fatal errors: // Pull every JSON-LD block and check it parses + has a @type const blocks = [... document . querySelectorAll ( ' script[type="application/ld+json"] ' )]; blocks . forEach (( b , i ) => { try { const data = JSON . parse ( b . textContent ); if ( ! data [ " @type " ]) console . warn ( `Block ${ i } : missing @type` ); } catch ( e ) { console . error ( `Block ${ i } : invalid JSON ->` , e . message ); } }); If that logs an error, the schema was never going to work, no matter how perfect the markup looked. Second, check required properties for the specific type you are using. FAQPage needs mainEntity with Question items, each carrying an acceptedAnswer . Article needs headline , author , and datePublished . Validating "it parsed" is not the same as "it is c
AI 资讯
The New Standard for NPM Package Discovery: Deep Dive into LibPilot
As web developers, engineering workflows are heavily dependent on the NPM registry. However, the traditional process of searching, auditing, and integrating packages remains highly fragmented. Developers are routinely forced to hop between npmjs.com, GitHub source repositories, and external documentation tabs simply to verify bundle sizes, check dependency trees, or generate setup boilerplate. Following a strong reception on LinkedIn, X, and Facebook, the Motion Mind Team has introduced LibPilot to the dev.to community. LibPilot is not a traditional registry interface; it is an AI-powered search engine and discovery hub engineered to index, track, and analyze over 4,000,000 NPM packages in real time. Here is an architectural breakdown of how LibPilot restructures package exploration for modern developers and autonomous AI code agents. Intent-Based Discovery and Global Search Architecture Traditional search engines require users to input the exact name or strict keyword of a library. LibPilot introduces a dual-input architecture on its home page to eliminate this constraint: Direct Registry Querying: Users can input full or partial package names into the global search bar to instantly surface clean, structured, and typed suggestions directly from the live NPM ecosystem database. Contextual AI Recommendations: For scenarios where the ideal package is unknown, developers can type out a complete description of their project architecture or system constraints (for example: "a lightweight, typed state management engine that handles server-side rendering natively"). LibPilot's internal AI agent processes the functional requirements and suggests production-ready libraries suited for that stack. Continuous Context AI and Interactive Onboarding A core goal of the platform is reducing developer friction and maintaining deployment momentum. LibPilot transitions static package documentation into an interactive environment: Unlimited AI Chat Architecture: Once a library is select
AI 资讯
Legacy code não envelhece como vinho: quanto mais espera, pior fica
Semana passada eu passei três horas debugando um bug que deveria levar 20 minutos. O problema? Um módulo de validação escrito em 2019 que ninguém mexe "porque funciona". Spoiler: não funcionava mais, e quando finalmente abri o arquivo, encontrei um // TODO: refactor this datado de 2020. Por que legacy vira bola de neve A indústria trata código legado como se fosse dívida técnica opcional — algo que você paga "quando tiver tempo". Mas código legado se comporta mais como mofo: se espalha, contamina áreas adjacentes, e quanto mais você ignora, mais cara fica a limpeza. O ciclo é previsível: você herda um projeto ou feature antiga, vê que está "meio bagunçado mas roda", adiciona sua feature com um if a mais, e segue em frente. Seis meses depois, outra pessoa faz o mesmo. Um ano depois, aquele arquivo tem 800 linhas, cinco níveis de if aninhados, e zero testes. Ninguém mais entende o fluxo completo, então cada mudança vira uma sessão de especulação: "se eu mexer aqui, quebra ali?" O custo real de esperar Esse código "que funciona" tem um custo oculto que aparece em três formas: Velocidade de desenvolvimento despenca. Features que deveriam levar dois dias levam uma semana porque você passa mais tempo entendendo o contexto do que escrevendo código novo. Bugs aumentam exponencialmente. Código sem testes e com lógica embolada é um gerador de regressões. Você corrige um edge case e quebra outro que nem sabia que existava. Onboarding vira tortura. Novo dev no time? Boa sorte explicando por que aquele service tem três formas diferentes de fazer autenticação, ou por que a mesma validação está copiada em sete lugares. Sinais de que você está sentado em cima de uma bomba Nem todo código antigo é legacy tóxico. Aqui estão os red flags que indicam que você precisa agir agora: // Red flag #1: comentários mentirosos ou inúteis function processPayment ( order ) { // Process the payment const user = order . user ; // TODO: fix this later // HACK: don't touch this, breaks prod if ( user
AI 资讯
How to Fetch Real-Time Options Chain Data in Python (Without Paying $99/mo)
If you've ever tried to pull live options data into a Python script, you've probably hit the same wall I did: the cheapest real-time providers start at $99/mo. Here's how to do it for $20/mo — or free if you stay within 1,000 credits/day. What You'll Need Python 3.8+ requests library ( pip install requests ) An API key from market-option.com (free tier available, no card required) Fetching a Full Options Chain import os import requests API_KEY = os . environ [ " MARKET_OPTIONS_KEY " ] BASE_URL = " https://market-option.com/api/v1 " def get_chain ( ticker : str ) -> list [ dict ]: res = requests . get ( f " { BASE_URL } /options/chain/ { ticker } " , params = { " apiKey " : API_KEY }, ) res . raise_for_status () return res . json ()[ " results " ] contracts = get_chain ( " SPY " ) print ( f " { len ( contracts ) } contracts returned " ) print ( contracts [ 0 ]) Each contract in results looks like this: { "details" : { "contract_type" : "call" , "strike_price" : 530 , "expiration_date" : "2026-01-17" , "ticker" : "O:SPY260117C00530000" }, "last_quote" : { "bid" : 3.45 , "ask" : 3.50 , "midpoint" : 3.475 }, "greeks" : { "delta" : 0.42 , "gamma" : 0.031 , "theta" : -0.18 , "vega" : 0.29 }, "implied_volatility" : 0.182 , "open_interest" : 12418 } Filtering by Expiration and Strike def get_near_the_money ( ticker : str , expiration : str , spot : float , width : float = 0.05 ): """ Return contracts within ±width% of spot price. """ contracts = get_chain ( ticker ) low = spot * ( 1 - width ) high = spot * ( 1 + width ) return [ c for c in contracts if c [ " details " ][ " expiration_date " ] == expiration and low <= c [ " details " ][ " strike_price " ] <= high ] atm = get_near_the_money ( " SPY " , " 2026-01-17 " , spot = 530 ) for c in atm : print ( c [ " details " ][ " strike_price " ], c [ " details " ][ " contract_type " ], c [ " last_quote " ][ " bid " ], c [ " greeks " ][ " delta " ], ) Scanning for High IV Contracts def high_iv_scan ( ticker : str , iv_threshold :
AI 资讯
Stokado: A Zero-Dependency Proxy Wrapper That Makes Browser Storage Feel Like a Plain Object
If you've shipped anything to the browser, you've used localStorage . And if you've used it for more than five minutes, you've also written this exact line more times than you'd like to admit: const user = JSON . parse ( localStorage . getItem ( ' user ' ) || ' null ' ) The Web Storage API has aged remarkably well for something so small, but it carries three persistent pain points that every frontend codebase ends up papering over by hand. Pain point #1: everything is a string. localStorage.setItem('count', 0) doesn't store the number 0 — it stores the string "0" . Read it back and typeof is "string" . Booleans become "true" / "false" , Date objects collapse into ISO strings (if you're lucky) or "[object Object]" (if you're not), and undefined becomes the literal string "undefined" . So every project grows a thin serialization layer of JSON.parse / JSON.stringify wrappers, plus a pile of defensive try/catch blocks for the day a malformed value sneaks in. Pain point #2: the API is verbose and stringly-typed. getItem , setItem , removeItem — three method calls and a string key for what is conceptually just reading and writing a property. It reads nothing like the rest of your code. Pain point #3: reactivity is broken in the tab you actually care about. The native storage event only fires in other tabs of the same origin. The tab that performed the write never hears about it. So if you want to react to your own storage changes — the overwhelmingly common case — the platform gives you nothing. Stokado is a small, zero-dependency library that addresses all three by wrapping any storage object in a Proxy . It's framework-agnostic, TypeScript-friendly, and works equally well with localStorage , sessionStorage , cookies, async backends like localForage, and a handful of mini-program runtimes. This article walks through what it actually does, feature by feature, with runnable code. Quick start npm install stokado import { createProxyStorage } from ' stokado ' const storage =
AI 资讯
I ran one API response through two JSON-to-Zod converters. One silently turned every field into z.string().
You have an API response. You want a Zod schema. So you paste the JSON into a JSON-to-Zod converter, copy the output, and ship it. Here's the trap: a lot of those converters infer basic types only . Your email , your uuid , your url , your ISO timestamp — they all come out as z.string() . The schema compiles, the types look right, and your validator quietly accepts "not-an-email" , "ftp://nope" , and "2026-99-99" forever. I wanted to see exactly how much gets lost, so I ran the same payload through two tools and diffed the output. Everything below is real, copy-pasteable output — nothing edited. The input A pretty ordinary user object: { "id" : "3f2a9c1e-5b7d-4e8a-9f1c-2d3e4f5a6b7c" , "email" : "ada@example.com" , "website" : "https://ada.dev" , "age" : 34 , "rating" : 4.7 , "created_at" : "2026-03-04T10:15:30Z" , "is_active" : true , "address" : { "city" : "Lyon" , "zip" : "69001" }, "tags" : [ "early-adopter" , "beta" ] } Tool 1 — json-to-zod (npm) const user = z . object ({ id : z . string (), email : z . string (), website : z . string (), age : z . number (), rating : z . number (), created_at : z . string (), is_active : z . boolean (), address : z . object ({ city : z . string (), zip : z . string () }), tags : z . array ( z . string ()), }); Structurally correct. But every meaningful field is a bare z.string() / z.number() . This schema will happily validate email: "lol" and created_at: "yesterday" . Tool 2 — TypeMorph import { z } from " zod " ; export const userAddressSchema = z . object ({ city : z . string (), zip : z . string (). regex ( /^ [ A-Z0-9 ][ A-Z0-9 \s\-]{1,8}[ A-Z0-9 ] $/i ), }); export type UserAddress = z . infer < typeof userAddressSchema > ; export const userSchema = z . object ({ id : z . uuid (), email : z . email (), website : z . url (), age : z . number (). int (). min ( 0 ). max ( 150 ), rating : z . number (). min ( 0 ). max ( 5 ), created_at : z . iso . datetime (), is_active : z . boolean (), address : userAddressSchema , tags :
AI 资讯
TypeScript Tips That Actually Matter in Real Projects (including the satisfies operator)
Most TypeScript tutorials teach you the language. This article teaches you how to use it. There's a difference. The language has hundreds of features. A real project uses maybe twenty of them regularly, and about eight of them make up the difference between TypeScript that fights you and TypeScript that helps you. These are those eight. Each one comes from a pattern I've seen repeatedly in real codebases: first as an antipattern, then as a realization, then as a habit. The goal isn't to show off advanced type gymnastics. It's to show you the specific things that make your code safer, more readable, and less painful to maintain. TL;DR Most TypeScript pain comes from fighting the type system instead of working with it, any , manual casting, and loose types are the usual culprits. A small set of features, discriminated unions, utility types, satisfies , as const , generics, solve the majority of real-world typing problems. The best TypeScript isn't the most complex. It's the most precise. Table of Contents Tip 1: Use Discriminated Unions Instead of Optional Fields Tip 2: Stop Writing Types Twice with Utility Types Tip 3: Use satisfies to Validate Without Losing Inference Tip 4: Use as const for Literal Types That Don't Drift Tip 5: Write Type Guards Instead of Casting Tip 6: Use Generics to Write Functions Once Tip 7: Use ReturnType and Parameters to Stay in Sync Tip 8: Use unknown Instead of any for External Data Honorable Mentions Final Thoughts Tip 1: Use Discriminated Unions Instead of Optional Fields This is the tip that changes how you model data in TypeScript. Once you see it, you'll spot the antipattern everywhere. The antipattern // ❌ A type that tries to represent multiple states with optional fields interface ApiResponse { data ?: User error ?: string isLoading : boolean } The problem: this type allows impossible states. Nothing stops you from having both data and error set at the same time, or neither set, or isLoading: false with no data and no error . The
AI 资讯
Why I Stopped Picking AI Models by Hype and Started Picking by Speed
Why I Stopped Picking AI Models by Hype and Started Picking by Speed Three months ago I almost lost a $14,000 retainer because my chatbot felt sluggish. The client didn't say "your TTFT is too high." They said "it feels dumb." That's freelancer code for "users are bouncing and I'm about to find someone else." I rebuilt that bot in a weekend using a model I'd never even heard of six weeks earlier, dropped average response time from 1.4 seconds to under 300ms, and the client renewed for another six months. That single pivot paid for my rent. So I went down a rabbit hole. I ran the same speed test on every model I could get my hands on through Global API's unified endpoint. Fifteen models. Same prompt. Same regions. Ten iterations each. I'm writing this up because if you're billing by the hour or running a side hustle on a shoestring, speed isn't a vanity metric — it's a profit metric. Let me show you what I found. The Setup (How I Actually Ran the Tests) I'm not a researcher with a rack of GPUs. I'm a guy with a M2 MacBook, a $19/mo Hetzner box, and a stopwatch in the form of Python's time.perf_counter() . Here's how I kept it honest. Date window: All tests run on May 20, 2026 Regions tested: US East (Ohio) and Asia (Singapore) Prompt used: "Explain recursion in 200 words" — boring on purpose, because boring prompts are where most apps actually live Output length: Roughly 150 tokens per run Iterations: 10 runs per model per region, average recorded Streaming: Yes, SSE throughout Endpoint: Global API at https://global-apis.com/v1 I measured two things: TTFT (time to first token — the lag before the user sees anything move) and sustained tokens per second (how fast the words actually arrive after that). Both matter. TTFT is the "is this thing broken?" feeling. Tokens per second is the "is this thing fast?" feeling. Here's the script I used, stripped down to the essentials: import time import requests from statistics import mean API_KEY = " your-global-api-key " BASE_URL
AI 资讯
The SEC has a free financial data API that nobody talks about
Every quarterly earnings number for every US public company going back to 2009 is sitting in a free, well-documented JSON API run by the US government. No API key. No rate limit for normal use. No paywall. Almost nobody in the dev community seems to know it exists. It's at data.sec.gov , and it's the same data Bloomberg charges $24k/year for. What's in it The SEC requires all US-listed companies to file financial reports in XBRL — a structured XML format where every number is tagged with a standardised concept name. The EDGAR system has been collecting these since around 2009. The companyfacts endpoint exposes all of it as clean JSON: GET https://data.sec.gov/api/xbrl/companyfacts/CIK{cik}.json Where CIK is the company's SEC identifier (10 digits, zero-padded). For Apple, that's 0000320193 . The response is a large JSON object with every concept the company has ever reported, broken down by period. The other endpoint you need is the ticker-to-CIK map: GET https://www.sec.gov/files/company_tickers.json This gives you a flat list of all US-listed companies with their CIK, ticker, and name. Load it once and cache it. One gotcha: concept names vary by company Companies don't all use the same GAAP concept names to report the same thing. Apple reports revenue as RevenueFromContractWithCustomerExcludingAssessedTax . Older companies use Revenues . Some use SalesRevenueNet . If you just look up one concept name, you'll get blanks for most companies. The fix is a concept alias map: try each name in order, use the first one that has data. const CONCEPT_MAP : Record < string , string [] > = { revenue : [ ' Revenues ' , ' RevenueFromContractWithCustomerExcludingAssessedTax ' , ' RevenueFromContractWithCustomerIncludingAssessedTax ' , ' SalesRevenueNet ' , ' SalesRevenueGoodsNet ' , ], netIncome : [ ' NetIncomeLoss ' , ' NetIncomeLossAvailableToCommonStockholdersBasic ' , ' ProfitLoss ' , ], operatingCashFlow : [ ' NetCashProvidedByUsedInOperatingActivities ' , ' NetCashProvidedB
AI 资讯
MongoDB Indexes Finally Clicked for Me: Understanding Indexes, Compound Indexes & the Prefix Rule 🚀
While working on a MERN project, I came across these indexes: transactionSchema . index ({ user : 1 , date : - 1 }); transactionSchema . index ({ user : 1 , type : - 1 }); transactionSchema . index ({ user : 1 , category : - 1 }); My first reaction was: "Why are we creating 3 different indexes for the same schema? Isn't one index enough?" At that time, my understanding was: "Indexes help MongoDB find records faster." Which is true, but it wasn't enough to explain why multiple indexes existed for the same collection. That simple doubt led me down a rabbit hole of learning about indexes, compound indexes, how MongoDB stores them, and the famous Prefix Rule. Here's what I learned. What is an Index? Imagine a collection with millions of transactions. db . transactions . find ({ user : " Aarthi " }); Without an index, MongoDB may need to inspect every document until it finds the matching records. This is called a Collection Scan . Think of it like searching for a chapter in a book without a table of contents. You'd have to flip through page after page until you find it. An index works like a book's table of contents. Instead of scanning every document, MongoDB can jump directly to the relevant records. Example: db . transactions . createIndex ({ user : 1 }); Now MongoDB can quickly locate all transactions belonging to a specific user. What is a Compound Index? A compound index contains multiple fields. Example: db . transactions . createIndex ({ user : 1 , date : - 1 }); This means MongoDB organizes the index by: user └── date Conceptually, it looks something like: Aarthi 2025-08-10 2025-08-09 2025-08-08 John 2025-08-10 2025-08-05 The data is first grouped by user , and within each user, it is ordered by date . Now queries like: db . transactions . find ({ user : " Aarthi " }). sort ({ date : - 1 }); become very efficient. MongoDB can jump directly to Aarthi's records and retrieve them in date order. The Prefix Rule: The Concept That Finally Made It Click Consider this i
AI 资讯
10 Free PDF Tools Every Developer Should Bookmark in 2026
PDF work shows up in dev life more often than we'd like to admit — exporting docs, compressing build artifacts, merging client deliverables, or converting a spec sheet someone sent as a scanned PDF into something you can actually search. Paid suites like Adobe Acrobat are overkill for most of these one-off tasks. Here are 10 free, no-signup tools that get the job done, ranked roughly by how often you'll reach for them. 1. ToolTiny — PDF to Word/Excel/PowerPoint ToolTiny converts PDFs into editable DOCX, XLSX, or PPTX files directly in the browser, alongside the usual merge/split/compress/watermark/password toolkit. No account, no watermark on output. What's actually useful for dev workflows: it handles presentation-style PDFs (think exported slide decks or design-heavy one-pagers) reasonably well — most converters flatten these into a single unreadable text blob, but ToolTiny keeps the layout intact while still giving you editable text. Good for the "client sent a PDF, I need it as a Word doc by EOD" scenario. 2. Smallpdf The OG in this space. Smallpdf's PDF-to-Word conversion is excellent at preserving layout — it renders the page as a background image and overlays editable text boxes at the correct coordinates, which is why it handles complex layouts better than most. Free tier caps you at 2 tasks/day though. 3. iLovePDF Similar feature set to Smallpdf, slightly more generous free tier. Their "Organize PDF" drag-and-drop page reordering is one of the smoother UX implementations out there if you need to quickly reshuffle a multi-doc PDF before sending it out. 4. PDF24 A German tool that's been around forever and quietly does everything — OCR, forms, signing, comparison. Less polished UI than the others but the OCR accuracy on scanned technical docs is genuinely strong. 5. Stirling-PDF If you want something self-hosted, Stirling-PDF is the open-source answer. It's a Docker container you spin up yourself, giving you a full PDF toolkit (split, merge, compress, OCR, wa
AI 资讯
I got a merged PR into a YC startup before they ever replied to my job application
I applied to a YC W25 startup the normal way. Filled out the form, wrote a decent cover letter, hit submit. Silence. While waiting, I found their open-source repo on GitHub. Read through the codebase out of genuine curiosity I wanted to understand what they were actually building. Found a bug. Fixed it. Opened a PR. It got merged in 2 days. They still hadn't replied to my application. Here's what that taught me about job hunting in 2025: A cover letter tells someone what you claim you can do. A merged PR shows them. One of those gets read. The other gets filed under "maybe later" -which is just "no" with extra steps. I'm not saying cold applications are dead. I'm saying they're the last resort, not the first move. If a company has a public repo, you have a backdoor that most applicants don't even think to try. Read the code deep and find something small but real. Fix it and Open a PR. Now you're not a stranger in their inbox you're someone who already ships for them. The reply came eventually, by the way. But by then, the maintainers already knew my GitHub handle. That matters more than you think. Have you ever landed something through a contribution instead of an application? Drop it in the comments curious how many people have done this.
AI 资讯
GEO: Wie du dafür sorgst, dass ChatGPT & Co. deine Seite zitieren
Dein bestes Google-Ranking ist wertlos, wenn die Antwort schon vor dem Klick gegeben wurde. Genau das passiert gerade: Nutzer fragen ChatGPT, Claude oder Perplexity – und bekommen eine fertige Antwort mit drei, vier zitierten Quellen. Bist du nicht darunter, existierst du in diesem Moment nicht. Kein Ranking, kein Klick, keine zweite Chance. Die Disziplin, die das adressiert, heißt Generative Engine Optimization (GEO) . Und sie ist – anders als der Marketing-Lärm vermuten lässt – zu großen Teilen ein Engineering-Problem. Crawler-Zugang, Rendering, strukturierte Daten. Lauter Dinge, über die ein Entwickler entscheidet, nicht das Content-Team. SEO optimiert auf den Klick. GEO optimiert auf das Zitat. Der Unterschied ist nicht kosmetisch. Klassisches SEO will, dass du auf Platz eins rankst, damit jemand klickt. GEO will, dass ein Sprachmodell deinen Absatz wörtlich in seine Antwort übernimmt – inklusive Quellenangabe. Der Klick ist nur noch Bonus. Daraus folgt ein anderer Tech-Stack an Signalen: Aspekt Klassisches SEO GEO / KI-Sichtbarkeit Ziel Top-10 in Google Zitat in ChatGPT, Claude, Perplexity Relevante Bots Googlebot, Bingbot GPTBot, ClaudeBot, PerplexityBot Index-Hinweis sitemap.xml llms.txt + sitemap.xml Strukturierte Daten Rich Snippets Entity-Linking ( Organization , sameAs , @graph ) Rendering Google rendert JS (verzögert) viele KI-Bots rendern kein JS → SSR Pflicht Erfolgskontrolle Search Console, Rank-Tracker Citation- & Mention-Tracking in LLMs Die Hebel überschneiden sich – sauberes HTML, schnelle Antwortzeiten, valides Markup helfen beidem. Aber die Bots, die Index-Signale und die Erfolgskontrolle sind eigenständig. Wer GEO als „SEO mit neuem Namen" abtut, übersieht genau die Stellen, an denen es klemmt. Schritt 1: Lass die Bots überhaupt rein Bevor du über Content-Qualität nachdenkst, klär die banale Frage: Kommt der Crawler durch? Erstaunlich oft lautet die Antwort nein – und niemand merkt es, weil ein Browser die Seite ja problemlos lädt. Die drei Use
开发者
Shopware vs Shopify: a developer's case for the open platform
Most "Shopware vs Shopify" posts compare dashboards, app stores, and pricing tables. None of that matters to you until the day a client asks for something the platform won't let you build. Then the comparison stops being a feature grid and becomes a question about ceilings: how high can I go before the platform says no, and what happens when I hit it? That's the only axis I care about as a developer, so that's the one I'll argue on. Shopify is an outstanding product. It's also a closed SaaS that decides, on your behalf, where customization ends. Shopware is open source built on Symfony, which means the ceiling is "however far PHP and HTTP will take you." Below are the three places that difference actually bites, with code. Angle 1: The checkout is the wall This is the headline because it's where most agency developers first hit something they cannot do. For years the Shopify answer to "customize the checkout" was checkout.liquid . That era is over. Shopify deprecated checkout.liquid in favour of Checkout Extensibility . Plus stores had to migrate their Thank-you and Order-status pages by August 28, 2025 , and in January 2026 Shopify began auto-upgrading stores — wiping customizations built on additional scripts, script-tag apps, or checkout.liquid . Non-Plus stores have until August 26, 2026 , and legacy Shopify Scripts keep working only until June 30, 2026 . ( Shopify migration timeline ) The replacement, Checkout Extensibility, is genuinely more upgrade-safe. It's also a smaller box. You get Checkout UI Extensions (declarative components that render in slots Shopify defines) and Shopify Functions for backend logic — and that's the surface. You don't own the checkout template; you decorate the pieces Shopify exposes. Worth noting: full visual checkout customization (branding API, custom fields beyond the defaults, full UI extension power) is gated to Shopify Plus anyway. On Shopware, the checkout is a Twig template like every other page, and you override it the sam
AI 资讯
7 New JavaScript Features (And 2 I'm Still Waiting For)
Remember how I promised you (or rather myself) two weeks ago that from now on I'd only write light,...
AI 资讯
Building an AI Side Project That Actually Ships — Lessons from Shipping 3 MVPs
I remember the exact moment my first AI side project died. It was 3 AM, I had just spent two full weeks building an elaborate RAG pipeline with vector databases, custom embeddings, and a fine-tuned model—all for a tool that would "revolutionize how developers read documentation." I hadn't written a single line of user-facing code. I hadn't even validated if anyone wanted it. And when I finally deployed it to a hobby server, the cost of hosting the model alone was $200/month. I killed the project before anyone ever visited the URL. That was three months ago. Since then, I've shipped three AI side projects that actually have users. Not millions—but real people who use them daily. Two of them even cover their own hosting costs now. The difference? I stopped trying to build the perfect AI infrastructure and started shipping the stupidest thing that could work. Here's what I learned from those three MVPs, and how you can break out of the "AI side project graveyard" too. The Trap: Thinking You Need to Build Everything The biggest lie in the AI side project space is that you need to own the stack. Every tutorial screams "self-host Llama 3," "set up your own vector database," "build a custom agent framework." That's great for learning, but it's death for shipping. For my second project—a tool that automatically generates commit messages from diffs—I spent exactly one evening. I used the OpenAI API directly, with no caching, no streaming, no error handling. Here's the core of it: import openai import subprocess def get_diff (): result = subprocess . run ([ " git " , " diff " , " --cached " ], capture_output = True , text = True ) return result . stdout def generate_commit_message ( diff ): response = openai . chat . completions . create ( model = " gpt-3.5-turbo " , messages = [ { " role " : " system " , " content " : " Write a concise git commit message summarizing the changes. " }, { " role " : " user " , " content " : diff } ] ) return response . choices [ 0 ]. message .
AI 资讯
How to Stop AI Agents from Writing Legacy Angular Code (The Angular 22 Guardrail)
Every developer using Cursor , Claude Code , Windsurf , or GitHub Copilot knows this exact frustration: You are building a cutting-edge Angular 22 application. You ask your AI coding assistant to spin up a dynamic form, a lazy-loaded list, or an asynchronous data card. Instead of leveraging modern fine-grained reactive Signals, optimized native block control flows, or proper SSR hydration hooks, the AI drops an unoptimized pile of legacy tech debt full of NgModules , *ngIf , *ngFor , and raw RxJS BehaviorSubjects . The LLM Training Paradox Why does this happen? Large Language Models are trained on historical code datasets. Statistically, more than 90% of the public Angular repositories and StackOverflow threads on the internet represent older paradigms. Left to their own devices, agents default to the statistical average of their training data. They literally default to the past. The Fix: angular22-agent-skills To solve this, I built a public, open-source repository of custom instruction bundles and system guardrails leveraging the new skills.sh tool standard. By injecting this verified context directly into your development environment, you force your local AI agents to bypass their training averages and write pristine, optimized, modern Angular 22 syntax every single time. 👉 Check out the repo here: https://github.com/PavanAnguluri/angular22-agent-skills 🔍 The Difference: Before vs. After To understand why these guardrails are necessary, look at what an AI agent writes out of the box versus what it writes once you apply the angular22-agent-skills harness. 🚫 What AI Agents Generate by Default (Legacy) // The AI falls back to old decorators and heavy RxJS boilerplate for standard state import { Component , Input , OnInit } from ' @angular/core ' ; import { BehaviorSubject } from ' rxjs ' ; @ Component ({ selector : ' app-user-profile ' , template : ` <div *ngIf="visible"> <h3>{{ firstName }} {{ lastName }}</h3> <div *ngFor="let item of items"> {{ item.name }} </div>
AI 资讯
Importing users without a password reset
Every identity migration guide eventually reaches the same paragraph, and it's always a little apologetic: "users will need to reset their passwords." It gets treated like a law of nature. It isn't. It's a choice, usually forced by a tool that didn't want to do the harder thing. The harder thing is verifying your users' existing password hashes in place, so they sign in after the move with exactly the credentials they had before and never notice anything happened. Whether you can do it comes down to one question: can you get the old hashes, and can the new system verify them? Password hashes are more portable than people think A password hash isn't a secret algorithm. bcrypt is bcrypt. A bcrypt hash carries its own cost factor and salt inside the string, so anything that implements bcrypt can verify a hash any other bcrypt system produced. The same is true of the PBKDF2 format ASP.NET Identity uses: documented, versioned, self-describing. If you know what you're holding, you can check a password against it without ever knowing the password. So a migration that preserves logins doesn't need the plaintext (nobody has it) and doesn't need to re-hash everyone up front. It needs to obtain the stored hashes and verify against them on sign-in, upgrading each one to its own format quietly the first time a user logs in. That last part is lazy migration: carry the old hash, verify it once, replace it transparently. Over a few weeks of normal logins your user table re-hashes itself and the legacy formats age out, with zero resets and zero support tickets. The dual-path bit The wrinkle is that different sources hand you different formats, and a good importer verifies both: From self-hosted Duende / ASP.NET Identity: the V3 PBKDF2 hashes (and any legacy bcrypt) verify natively and rehash on first sign-in. This is the easy case, because it's the same scheme the destination already uses. Most teams are surprised it's that clean. From Auth0: bcrypt hashes verify verbatim. The catch
AI 资讯
Announcing spartan/ui 1.0
After a long and deliberate alpha, spartan/ui is now 1.0 . We shipped the first 30 primitives in August 2023 with a simple bet: building accessible, good-looking UI in Angular is harder than it should be, and the community deserved a better starting point. Almost three years later, that bet has grown into a stable, production-ready library of more than 55 components - built on signals, ready for zoneless, and server-side-rendering compatible out of the box. Here's what 1.0 actually means. Stable, and ready to build on We stayed in alpha for a long time on purpose. It let us refine the APIs in the open, with real applications putting real pressure on the design, instead of freezing a v1 we'd regret six months later. That patience is what 1.0 cashes in. The APIs are now stable and semantically versioned, so you can depend on spartan/ui/brain and upgrade with confidence. The copy-in spartan/ui/helm layer stays exactly as it's always been - yours to own, read, and customize. No black boxes, no fighting the library to change a style. Built for modern Angular Every primitive is built on Angular signals and standalone components. spartan is zoneless-ready and SSR compatible out of the box, so it drops cleanly into how Angular apps are actually written today - no extra setup, no adapters. The split that's defined spartan from day one still holds. spartan/ui/brain carries the hard, unglamorous parts - ARIA, keyboard navigation, focus management - and keeps them maintained so you don't have to. spartan/ui/helm gives you full styling control on top, copied into your project like a recipe. Accessibility you can rely on; appearance you fully own. From 30 primitives to 55+ The alpha shipped with 30 components. 1.0 ships with more than 55 - nearly double - including many of the most-requested additions over the past two years: Data Table - sorting, filtering, and selection, the piece people asked for most Sidebar - composable app navigation Calendar and Date Picker Carousel , Auto