AI 资讯
AI For Fun! Électrique Chats at Hack the Kitty, Built with Kiro.
A cat astrologer, spec-driven and running on Amazon Bedrock A companion to A Builder in Paris: Do Devs Dream of Électrique Chats? Last month I wrote about the idea. Six rainy days in Paris, a closed laptop, and a hackathon I did not mean to enter, and somewhere between the Musée de l'Orangerie and a lot of walking, an idea arrived. Cats are inscrutable. The people who love them are obsessed with understanding them anyway. Astrology is an old framework for making the unknowable feel readable, and maybe, just maybe, it helps us understand them a little. Her name is Madame Minou , a French cat astrologer who reads your cat's stars from a café terrace. That first article was the idea . This one is the build. Vibe-coded, but on rails Was it vibe-coded? You know it! AI wrote the lines, and I said "no, not like that" more times than I can count. But it was vibe-coding on rails, and the rails were Kiro. Before a single line of app code, I wrote the requirements in EARS notation, a design doc, and a build-ordered task list, all living in .kiro/specs . Decide what "done" means before letting anyone, human or model, start building. The specs are what kept the vibes on track. Then the steering files. .kiro/steering held the enduring rules of the project: product principles, security guardrails, technical direction, and UI law. These were the thing that kept a long, multi-session build from drifting. When a new session opened, the steering files were already the shared context. "The café blue" was one token, not five guesses. Security was not optional. The garbled café sign was a deliberate easter egg, not a bug to fix. From there, the loop: Kiro implemented one approved block at a time, ran each task's PASS/FAIL QA gate on itself before moving to the next, and only stopped for my review on the two things that actually mattered. I directed and approved. Kiro proposed and built. Spec first, block by block, human in the loop. The facts are sacred Here is the part that looks like a
AI 资讯
Building Instant Translation Assistance for Book Translations with Python and LLMs
How we integrated real-time phrase translation feedback into our AI-powered book translation workflow, and what we learned about latency, context, and prompt engineering. When we launched LectuLibre, our AI-powered book translation platform, users loved the quality of full-chapter translations. But they kept asking for something else: while reading a partially translated book, they'd stumble on an untranslated phrase or an awkward auto-translation and want to quickly get a better version without leaving the page. So we built 即时翻译求助 (Instant Translation Help)—a feature that lets readers highlight any phrase and get a context-aware, human-quality translation within seconds, along with a brief explanation of tricky parts. Here's how we built it, the technical challenges we faced, and the lessons we learned about stitching LLMs into a real-time reading experience. Problem: Real-time, Context-Aware Translation Inside a Book Most web apps offer generic translation via API calls—send a sentence to Google Translate, get a result. But that doesn't work for literary texts. A phrase like "She let the cat out of the bag" needs to be translated idiomatically, and the appropriate rendering depends heavily on the surrounding paragraphs (is the tone formal? sarcastic? part of a metaphor chain?). Our existing translation pipeline processes entire chapters in bulk with carefully crafted prompts, but for instant help, we needed sub-second latency while preserving that same depth of context. Our Approach: Server‑Sent Events and a Smart Prompt Buffer We chose Server-Sent Events (SSE) over WebSockets because the communication is one-directional (server pushes translation tokens) and SSE is simpler to implement with FastAPI. The client (a React app) sends a POST request with: The phrase to translate The book ID and the exact location (chapter/paragraph index) The target language Our backend retrieves the surrounding text from PostgreSQL (we store the original book in chunks), feeds a care
AI 资讯
# What Happens When You Try to Build a Lawyer for Someone Who Can't Afford One?
The Problem That Wouldn't Leave Me Alone Pakistan has 220 million people. A functioning legal system. Hundreds of Acts, ordinances, and constitutional provisions that technically protect every citizen. Almost nobody can use them. The median lawyer's consultation fee in Karachi is more than what many families earn in a week. Legal aid is understaffed and geographically concentrated in major cities. And the laws themselves? Written in English — a language most of the population reads functionally at best, and doesn't speak at home at all. So when a landlord illegally locks someone out. When a factory worker gets fired without severance. When a woman wants to know her inheritance rights. When a tenant needs to understand what "Section 16 of the Rent Restriction Ordinance" actually means for their specific situation — they either find a lawyer they can't afford, ask someone who doesn't really know, or quietly give up. This isn't a knowledge problem. It's an access problem. I'm a CS student at Sukkur IBA University in interior Sindh — not Karachi, not Islamabad. The kind of city where you feel the gap between what the law says and what people actually know it says every single day. That gap is where HAQ started. HAQ is an Arabic and Urdu word. It means right — as in, what is rightfully yours. The name felt important. The Core Idea: Ask the Law, Get the Law There's a specific failure mode with AI and legal questions that drove every design decision I made, and it's worth naming clearly. Standard LLMs — any of them — will answer legal questions confidently. They'll cite "Section 144" or "the Transfer of Property Act" with total authority. They are often wrong. Sometimes subtly: the section exists but doesn't say what the model claims. Sometimes obviously: the Act doesn't apply in that province. Always uncitable: the user has no way to verify without finding the source themselves. For an accessibility tool, a confidently wrong answer isn't neutral. It's actively dangerous.
AI 资讯
Fix Your "Developer Slouch": Building a Real-time AI Posture Monitor with MediaPipe and Electron
We’ve all been there. You start your morning feeling like a Productivity God, sitting straight and typing at 120 WPM. Fast forward four hours, and you've morphed into a literal shrimp, face inches away from the monitor, hunting for a missing semicolon. 🦐 In this era of remote work, real-time posture correction and computer vision for health have become more than just "cool projects"—they are spinal lifesavers. Today, we’re going to build a desktop application using MediaPipe , WebRTC , and Electron that monitors your neck angle and sends a desktop notification the moment you start slouching. By leveraging MediaPipe Pose and TensorFlow.js , we can calculate the Forward Head Posture (FHP) ratio with surgical precision directly in the browser environment. The Architecture 🏗️ Before we dive into the code, let’s look at how the data flows from your webcam to that "Sit up straight!" notification. graph TD A[Webcam Feed] -->|MediaStream| B(WebRTC API) B -->|Video Frames| C[MediaPipe Pose Model] C -->|Landmarks| D{Geometry Engine} D -->|Calculate Ear-Shoulder Angle| E{Threshold Check} E -->|Angle > 30°| F[Electron Main Process] F -->|Trigger| G[System Desktop Notification] E -->|Healthy| H[Continue Monitoring] style G fill:#f96,stroke:#333,stroke-width:2px Prerequisites 🛠️ To follow along, you'll need the following tech stack: MediaPipe Pose : For high-fidelity body tracking. WebRTC : To capture the video stream from your webcam. Electron : To wrap our logic into a desktop app that runs in the background. TensorFlow.js : The backbone for running ML models in JavaScript. Step 1: Setting up the Video Stream (WebRTC) First, we need to grab the camera feed. In a modern browser environment (or Electron's Chromium), we use navigator.mediaDevices.getUserMedia . async function setupCamera () { const videoElement = document . getElementById ( ' input_video ' ); const stream = await navigator . mediaDevices . getUserMedia ({ video : { width : 640 , height : 480 }, audio : false }); v
AI 资讯
Why Arabic text comes out backwards when you extract it from a PDF (and how to fix it)
If you've ever built a feature that extracts text from PDFs, an Arabic-speaking user has probably filed this bug: "the words come out in reverse order." Not the letters — the words . Every line reads last-word-first. I spent the better part of a year fixing this class of bugs while building Confileo , a free PDF toolkit with first-class Arabic support. Here's what's actually going on, because almost every explanation online is wrong or incomplete. The four distinct failure modes People say "Arabic breaks" as if it's one bug. It's four: 1. Visual vs logical order (the reversed-words bug) A PDF doesn't store text the way a Word file does — it stores positioned glyph runs : "paint these shapes at these coordinates." For left-to-right scripts, the paint order happens to match the reading order, so naive extraction works by accident. Arabic is right-to-left. Many PDF generators emit the glyph runs in visual order — the order they appear on screen, left to right. A naive extractor concatenates the runs as stored and produces every line word-reversed. The text was never "reversed" in the file; your extractor just assumed paint order == reading order. Fix: reconstruct logical order using glyph positions + the Unicode Bidirectional Algorithm (UAX #9), not the content-stream order. Libraries like PyMuPDF already return text in logical order — a common mistake is "fixing" that output by reversing it again, which is how you get double-reversed text. Rule of thumb: never reverse Arabic yourself. If it looks backwards, your rendering layer lacks bidi support; the data is usually fine. 2. Disconnected letters (the ransom-note bug) Arabic letters are contextual: ع renders differently in initial, medial, final and isolated positions, and letters join. That joining is applied at render time by a shaping engine (HarfBuzz being the standard). If any step of your pipeline round-trips text through a non-shaping renderer — a canvas library, a barebones PDF writer, an image caption filter
AI 资讯
Scrape Google Trends Without an API Key (Including the Scraper Flag Google Hands You)
Google Trends has no official API, and most wrapper libraries rot within months. But the Trends site itself runs on a keyless JSON API that anyone can call, and it serves the exact numbers you see in the UI. Here is the full recipe, including one gotcha where Google quietly labels your session a scraper. The two step flow Trends works in two steps. First you call explore , which returns a list of widgets, one per chart on the page, each with a signed token: GET https://trends.google.com/trends/api/explore ?hl=en-US&tz=0 &req={"comparisonItem":[{"keyword":"web scraping","geo":"US","time":"today 12-m"}],"category":0,"property":""} Then you call a widget data endpoint with that widget's request and token : GET https://trends.google.com/trends/api/widgetdata/multiline?hl=en-US&tz=0&req=<widget.request>&token=<widget.token> The widget kinds map to endpoints: TIMESERIES uses multiline , GEO_MAP uses comparedgeo , and both RELATED_QUERIES and RELATED_TOPICS use relatedsearches . The cookie trick Call explore cold and you get a 429. The API wants a NID cookie, and here is the counterintuitive part: you get it by requesting the public explore page first, and that page may itself respond 429 while still setting the cookie you need. const res = await fetch ( ' https://trends.google.com/trends/explore?geo=US&q=test ' ); // res.status may be 429. The Set-Cookie header is still there. const cookies = res . headers . getSetCookie (); Grab the cookie from the 429 response, retry explore , and everything works. Strip the anti JSON prefix Every Trends response starts with a junk line like )]}' to break naive JSON.parse calls. Drop everything up to the first newline: const body = await res . text (); const data = JSON . parse ( body . slice ( body . indexOf ( ' \n ' ) + 1 )); The scraper flag Here is the part I have not seen documented. Look inside the widget request object that explore returns to a keyless session: "userConfig" : { "userType" : "USER_TYPE_SCRAPER" } Google knows. And
AI 资讯
Engineering Geofencing: Lessons in Android Battery and Location Accuracy
It happened during a quiet, solemn moment in a community prayer hall. I was sitting in the third row, reflecting, when suddenly, a high-pitched ringtone shattered the silence. It wasn't my phone, but the ripple effect of embarrassment was immediate. Everyone looked around, shifting uncomfortably. That collective tension is something we have all felt—a moment of human error that technology should have intercepted. I looked at my own device, feeling the familiar anxiety of whether I had remembered to flip the physical silent switch. It was then that I decided to stop relying on my own memory. We live in an era of hyper-connected devices, yet the most basic context-awareness—knowing where we are and how our phone should behave—remains manual. I found myself constantly toggling between 'Normal' and 'Silent' modes at the library, the office, and the gym. If I forgot, I was the person disrupting a meeting. If I remembered to mute it, I inevitably forgot to unmute it, missing urgent calls from family for hours. The existing solutions were either too bloated, requiring invasive cloud permissions, or they simply failed to trigger reliably when the screen was off. I needed a solution that was local, predictable, and battery-conscious. Building Muffle started with the realization that I had to master the GeofencingClient API without draining the user's battery. The primary challenge wasn't just triggering an event; it was doing so while the device was in a deep sleep state. I initially experimented with a standard LocationManager approach, polling GPS coordinates at set intervals. That was a disaster. It kept the radio active, pinged the GPS satellites constantly, and decimated the battery life in under four hours. It was an immediate non-starter for a production-ready application. I pivoted to the Geofencing API provided by Google Play Services, which leverages the fused location provider. This is significantly more efficient because the system handles the batching and hardwa
AI 资讯
The only AI glossary you’ll need this year
The rise of AI has brought an avalanche of new terms and slang. Here is a glossary with definitions of some of the most important words and phrases you might encounter.
AI 资讯
The First .com Domain Was Symbolics.com
Every business that has ever typed a web address into a browser owes a small debt to a company most people have never heard of. On March 15, 1985, a computer maker called Symbolics registered Symbolics.com and, in doing so, became the first ever holder of a .com domain name. More than forty years later that address is still registered and still resolves - making it the oldest .com domain on the internet. Who was Symbolics? Symbolics Inc. was a Massachusetts company that built specialized computers called Lisp machines - workstations designed from the silicon up to run the Lisp programming language, then the darling of artificial intelligence research. These were serious, expensive machines aimed at labs and universities, and the company sat right at the cutting edge of 1980s computing. So it was fitting, if a little accidental, that they were first in line when commercial domains became available. The domain name system itself was brand new. DNS had only been introduced in 1983 to replace the unwieldy HOSTS.TXT file that every machine on the early internet had to keep in sync. The now-familiar top-level domains - .com , .org , .net , .edu , .gov - were defined in 1984. When registration opened, .com was meant for commercial entities, and Symbolics grabbed theirs before anyone else did. A slow start for the web's most valuable real estate What is striking today is how little demand there was. In the whole of 1985, only a handful of .com domains were registered - names like BBN, Think, and a few other technology companies trickled in over the following months. There was no gold rush, because there was no web yet. Tim Berners-Lee would not propose the World Wide Web until 1989, and the first website would not appear until 1991. A domain name in 1985 was a technical convenience for reaching a machine, not a brand or a piece of property. That makes Symbolics.com a kind of time capsule. It was registered before the web, before browsers, before e-commerce, and before anyon
开发者
개발 지식 없이도 외주 앱 개발 과정을 직접 점검하는 방법
개발 역량이 없어도 외주 개발사의 진행 상황을 명확하게 파악하고 피드백을 줄 수 있다. 필요한 건 기술 지식이 아니라, 올바른 협업 구조다. 이 글은 비개발자 제품 관리자가 바로 적용할 수 있는 투명한 피드백 루프와 점검 방식을 소개한다. 외주 개발에서 비개발자가 소외되는 이유는 무엇인가? 외주 개발사와 일을 시작하면 처음 며칠은 소통이 활발하다. 요구사항 정리, 계약, 착수 미팅까지는 순탄하다. 문제는 그 이후다. 개발이 시작되면 대화의 언어가 바뀐다. "API 연결 중", "백엔드 스키마 설계 단계", "프론트 컴포넌트 분리 작업"처럼 전공자가 아니면 체감하기 어려운 표현들이 보고서를 채운다. 이 상태에서 비개발자가 할 수 있는 질문은 사실상 하나뿐이다. "잘 되고 있나요?" 그리고 돌아오는 답도 하나다. "네, 잘 되고 있습니다." 이 구조는 어느 개발사가 나쁜 의도를 가져서 생기는 문제가 아니다. 개발자는 기술 언어로 사고하고, 비개발자는 결과와 흐름으로 사고한다. 이 두 언어 사이에 다리가 없을 때 소외가 생긴다. 그리고 이 소외는 감정의 문제가 아니라 실질적인 리스크다. 방향이 틀어진 채 몇 주가 흐르면, 다시 맞추는 비용은 처음보다 훨씬 커진다. 비개발자가 개발 과정을 직접 점검할 수 있는 구조가 필요한 이유가 여기 있다. 비개발자가 진행 상황을 파악할 수 있는 협업 구조란? 투명한 협업 구조는 "보고를 더 자주 받는 것"이 아니다. 받는 정보의 언어와 형식을 바꾸는 것이다. 포텐랩은 매주 진행 상황을 공유하는 주간 피드백 루프를 팀 표준으로 운영한다. 이 루프의 핵심은 세 가지다. 무엇이 완료됐는가 : 이번 주에 실제로 만들어진 것, 확인 가능한 것. 다음 주에 무엇을 만드는가 : 다음 단계에서 기대할 수 있는 결과물. 막힌 것이 있는가 : 결정이 필요하거나 확인이 필요한 사항. 이 세 가지가 매주 비개발자도 읽을 수 있는 언어로 정리된다면, 소통 구조는 이미 절반 이상 해결된 것이다. 기술 용어 없이 "로그인 화면 완성, 다음 주엔 상품 목록 화면 작업"이라고 쓸 수 있다면, 비개발자는 지금 어디쯤 왔는지 감을 잡을 수 있다. 주간 피드백 루프를 실제로 설계하는 방법 주간 루프가 형식적인 보고에 그치지 않으려면 구조가 있어야 한다. 아래는 포텐랩이 프로젝트마다 적용하는 주간 피드백 사이클의 구성이다. 1단계 — 주간 업데이트 문서 공유 매주 정해진 요일에 개발팀이 업데이트 문서를 공유한다. 이 문서에는 완료된 기능, 다음 주 작업 항목, 그리고 결정이 필요한 사항이 포함된다. 형식은 슬랙 메시지든, 노션 페이지든 팀이 합의한 채널이면 된다. 중요한 건 형식보다 주기와 언어다. 매주 같은 날, 비개발자가 읽을 수 있는 언어로. 2단계 — 화면으로 확인 가능한 결과물 공유 텍스트 보고만으로는 실제로 무엇이 만들어졌는지 체감하기 어렵다. 그래서 주간 업데이트에는 화면 캡처, 동영상 클립, 또는 테스트용 링크가 함께 제공된다. "로그인 기능 완료"라는 문장보다 실제 작동하는 화면을 보는 것이 훨씬 구체적인 판단 근거가 된다. 3단계 — 비개발자가 직접 테스트하는 시간 개발팀이 보여주는 것만 보는 게 아니라, 비개발자가 직접 써보는 단계가 필요하다. 이 과정에서 "버튼이 너무 작다", "이 순서가 직관적이지 않다" 같은 피드백이 나온다. 기술 지식이 없어도 할 수 있는 피드백이고, 이런 피드백이 제품의 방향을 바로잡는다. 4단계 — 다음 주 작업 범위 합의 이번 주 결과를 확인한 뒤, 다음 주에 무엇을 만들지 함께 정한다. 이 단계에서 비개발자는 우선순위를 조정할 수 있다. "이 기능보다 저 기능이 먼저 필요하다"는 판단을 매주 할 수 있는 구조다. 우선순위는 비개발자가 가장 잘 아는 영역이다. 비개발자가 개발 진행 상황을 점검하는 실질적인 기준은? 개발 진행 상황을 평가할 때 기술적 판단을 할 필요는 없다. 다음 세 가지 기준으로 충분히 점검할 수 있다. 점검 항목 확인 방법 좋은 신호 주의가 필요한 신호 완료 결과물 화면 또는 테스트 링크로 직접 확인 매주 눈에 보이는 결과물이 있음 텍스트 보고만 있고
AI 资讯
The browser wars aren’t about search anymore — here are the best alternatives to Chrome and Safari
We’ve compiled an overview of some of the top alternative browsers available today aiming to challenge Chrome and Safari.
AI 资讯
The Right Way to Pair AI With Terraform Plans
terraform plan is honest about what it's going to do. The problem is it's also verbose, repetitive, and full of cosmetic changes (like recomputed tags) mixed in with real ones (like a database instance scheduled for -/+ replace ). On a 400-line plan, the dangerous changes hide. This is the kind of task AI is actually good at: skimming structured text, flagging the entries that matter, ignoring the rest. But "paste plan into Claude" is not the workflow. There's a specific shape to this that works. Why people get this wrong The natural instinct is to copy the plan output and paste it into a chat: Terraform will perform the following actions : # aws_instance.web will be updated in-place ~ resource "aws_instance" "web" { id = "i-0abc123def456" ~ instance_type = "t3.small" - > "t3.medium" ... The model will respond with a sentence about each line. You'll scroll. You'll skim. You'll miss the -/+ replace on the database because it's in the middle of 30 routine updates. This is the same failure mode as pasting a wall of logs and asking "is anything wrong?" The model is too polite to skip things. You need to tell it to. The format that actually works: JSON terraform show -json tfplan outputs a structured representation of the plan that's much easier to reason about than the text format. Two reasons: The "actions" field is explicit. Each resource_change has a change.actions array — ["create"] , ["delete"] , ["update"] , or ["delete", "create"] for replace. No ambiguity. You can filter before pasting. With jq , you can extract only the dangerous changes, drop the noise, and feed a 20-line summary into the AI instead of a 400-line plan. Try this: terraform plan -out = tfplan terraform show -json tfplan > plan.json # Get just the dangerous changes jq '[.resource_changes[] | select(.change.actions | contains(["delete"])) | {address, type, actions: .change.actions}]' plan.json That's the AI's input. Compact, unambiguous, and pre-filtered to the changes that need a human decision.
AI 资讯
AI Code Review That Engineers Actually Trust: The Pipeline We Run on Every Pull Request
Bolting an LLM onto your pull requests is a weekend project. Building AI code review that your engineers don't disable within two weeks is the actual problem. The failure mode isn't missing bugs — it's crying wolf. Post twenty nitpicks and three hallucinations on someone's PR and they'll mute the bot forever. This is the pipeline we built on Mattrx to earn — and keep — that trust. Mattrx is our multi-tenant marketing-analytics SaaS: ~95k lines of C#, 11 engineers, and enough pull requests that senior-reviewer time was the bottleneck. We tried the naive thing first — pipe the changed file into a model, post the output — and watched the team stop reading it in nine days . TL;DR Dimension Human-only / naive AI (before) AI review pipeline (after) Coverage selective / whole-file dump every PR, diff-focused First-review latency ~6 hours (wait for a human) ~3 minutes (AI first pass) Context none / a naked file diff + call sites + conventions Reviewers one mega-prompt specialized dimensions, in parallel False positives ~35% (so it gets ignored) ~6% (adversarially verified) Merge control human, or nothing severity gate; human always decides Governance none gateway: audit, cost, secret redaction ~90 PRs/week across 11 engineers; the pipeline reviews 100%. First-pass review latency 6h → 3 min. False-positive rate ~35% → ~6% — the single number that decides whether the bot lives or dies. Escaped defects to production down ~40%; senior-reviewer time down ~30%. ~$0.05 per PR (cheap model for style, frontier only for correctness). The one mental shift: AI code review is not about finding issues — models find plenty. It's about not crying wolf . The product is trust, and trust is a false-positive-rate problem. Verify before you comment; let the AI propose and the human dispose. The naive approach — and why it collapses // BEFORE: dump the whole changed file into one prompt, post whatever comes back. foreach ( var file in pr . ChangedFiles ) { var text = await File . ReadAllTextAsyn
AI 资讯
PWA with React + Leaflet: lessons from a 31-city map app
Notes from shipping a real-world PWA in Korea: Vite SSG is worth it. SPAs lose Korean SEO because Naver's crawler doesn't fully render JS. Leaflet > Mapbox for cost-sensitive projects. Free OSM tiles + zero cost. Firebase Hosting rewrites let you have deep URLs (/city/suwon) without a backend. KakaoTalk share is non-negotiable in Korea — 70%+ of social traffic. PWA install banner: don't auto-prompt, let the user discover. Project: https://gyeonggi-currency-map.web.app?ref=devto&utm_source=devto&utm_medium=social&utm_campaign=multi-site-bot
AI 资讯
How to Test OAuth Recovery Emails Without Exposing Real Inboxes
OAuth recovery emails look harmless until you test them the lazy way. A team sends password reset links or recovery codes into one shared mailbox, confirms that something arrived, and marks the job done. From a security view, that test is too weak. It can hide token reuse, wrong-user delivery, or log retention that exposes sensitive account events. For non-production checks, I like using a disposable email address that belongs to one test run. Some teams build that inbox layer themselves, some use tempmailso, but the core principle is the same: isolate the recovery event, inspect it quickly, and delete the evidence you no longer need. That is helpful when Authentication and OAuth changes ship together. Why OAuth recovery emails deserve their own threat model Recovery email tests are not just "did the mail send?" checks. They sit on the edge of account takeover risk, so the message itself matters almost as much as the login flow. A decent threat model for these emails should ask: did the message reach only the intended inbox for this run? does the link or code expire when the product says it does? is the message revealing too much user data in subject lines or previews? can an older token still be used after a new recovery request? do logs or test fixtures keep the recovery secret longer than they should? This is where shared inboxes become dangerous in a subtle way. Even if nobody has bad intent, mixed test data makes it harder to prove which token belonged to which request. The same operational confusion shows up in email change confirmation checks , and it gets worse when the email can restore account access. OWASP recommends testing authentication recovery features with the same care as sign-in and session controls, because weak recovery paths are a common bypass route for stronger primary login defenses: https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html A safer test flow for recovery links and codes The cleanest pattern is one inbox
产品设计
Chevy built an all-American EV truck — why is nobody buying it?
The Chevy Silverado EV is a solid first draft of an EV pickup truck. Here's what could make it better.
AI 资讯
The Go Code Review Comments List: 10 Rules Every Reviewer Cites
Book: The Complete Guide to Go Programming Also by me: Hexagonal Architecture in Go — the companion book in the Thinking in Go series My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools Me: xgabriel.com | GitHub You open a pull request in a Go repo you just joined. Ten minutes later there are six comments on it. None of them are about your logic. They point at a capitalized error string, a receiver named this , a context stored on a struct. Each comment links the same page: the Go Code Review Comments wiki. That page is the closest thing Go has to an official style council. It grew out of the comments Go's own maintainers left on CLs for years, and most Go teams treat it as the default rulebook. The problem is that the wiki tells you what but rarely why , so the rules read like arbitrary taste. They aren't. Each one exists because the alternative bit somebody. Here are ten that reviewers cite the most, with the reason behind each. Everything below is idiomatic on Go 1.23+. 1. Error strings are lowercase and unpunctuated The rule: an error string should not be capitalized and should not end with punctuation. // wrong return fmt . Errorf ( "Failed to open config." ) // right return fmt . Errorf ( "failed to open config" ) The reason is wrapping. Go errors get concatenated. Your string is almost never the whole sentence a user reads; it's a fragment in a chain built with %w : return fmt . Errorf ( "load settings: %w" , err ) // -> "load settings: open config: permission denied" Capitalize your fragment and you get load settings: Open config: permission denied in the middle of a line. End it with a period and you get a period in the middle of a longer message. Lowercase, no trailing punctuation, and every fragment composes cleanly no matter where it lands in the chain. The exception is a string that begins with an exported name or acronym, which keeps its own case ( HTTP , TLS ). 2. Receiver types are consistent ac
AI 资讯
iOS and Android Have Different Goals for PWAs: The Real Difference Beyond Support or No Support
This is a reprint from my tips blog. You can find the original article here: [ https://tips.ojapp.app/en/ios-android-pwa-goal-difference-3/ ] Android vs. iPhone: Different Goals for PWAs When people talk about PWAs, the explanation often sounds like this: Android supports PWAs. iPhone does not support PWAs very well. If you only look at the PWA specifications, this explanation is easy to understand. Android Chrome supports manifest.json, Service Worker, installation, notifications, shortcuts, and many other PWA features quite strongly. iPhone Safari, on the other hand, does not behave the same way as Android. But after testing PWAs on both iPhone and Android many times, I started to see the difference in another way. More accurately, Android and iPhone have different goals for PWAs. That is the biggest difference I noticed while testing repeatedly. Android tries to turn web pages into apps Android PWAs are clearly designed in the direction of making web pages feel closer to native apps. The idea is to take a website opened in the browser and move it toward a native-app-like experience. This direction is very strong on Android. That is why many PWA-related features are well supported. manifest.json Service Worker Push notifications Install Prompt Shortcuts theme_color background_color maskable icons display modes orientation Of course, it is not perfect. Manifest cache can be stubborn, multiple PWAs on the same domain can become confusing, and real-device testing can still create plenty of traps. Even so, when you build according to the PWA specification, Android usually responds in a fairly straightforward way. For example, if you set display: fullscreen , the app feels much more full-screen. theme_color is often reflected in the toolbar color. orientation also works quite strongly on Android. In other words, for Android, a PWA is an app made from the Web . iPhone starts from the experience of placing web pages on the home screen iPhone is different. Even before the
AI 资讯
A Docker-Based AWS SES Smoke Test With Disposable Inboxes
Use Docker and a disposable mailbox to verify AWS SES delivery in CI before release without touching shared inboxes or leaking test mail. Shipping an app that "sent the email" is not the same as shipping an app that delivered a usable message. In AWS stacks, the miss usually shows up late: a container points at the wrong SES region, a preview environment uses stale credentials, or the message lands with broken links after templating. A disposable email address check is a simple final guardrail, and its easy to automate when your mail sender already runs in Docker. This is the pattern I recommend when teams are searching for temp mail com style testing, but need something cleaner for production-like CI. The goal is not to replace unit tests or the Amazon SES mailbox simulator . The goal is to prove that your real app container, with real environment wiring, can send one controlled message and that the inbox content is worth shipping. Why this check belongs in the release path AWS SES is strict in useful ways. If your account is still in the sandbox, you can only send to verified identities or the mailbox simulator . Even after production access, delivery problems still happen becuase the application layer and the cloud layer drift separately. A practical smoke test catches things that mocks often miss: wrong AWS_REGION or SES endpoint selection missing secrets in the container runtime template variables that render empty in preview builds broken confirmation links caused by bad environment URLs unexpected sender identity changes between staging and release That list looks basic, but it doesnt stay basic when three services, one worker, and a release job all touch outbound email. A small Docker pattern that keeps the test deterministic Keep the sender in the same Docker image you deploy, but trigger a narrow email scenario from CI. For example, seed a temporary user, call the notification path once, then poll a disposable inbox API for the matching subject line. servi
AI 资讯
Five Things to Check When Delivering Fast
By Vilius Vystartas This is the follow-up to What Actually Changed in Two Weeks . That one was about setting up a project for AI-speed delivery. This one is about something I keep re-learning on every fast delivery. You start shipping faster with AI. The code works, the feature lands, it feels good. Then a few weeks later the feedback comes back, and some of it catches you off guard. Not because anything is broken — but because a few things that seemed obvious to you weren't obvious to the other side. No drama. It happens. Here are five things I'm learning to check earlier. 1. What does "done" look like from their side? To me, done means working software. To someone else it might mean pixel-match with a design. Both are valid. What helps: A quick "what does good enough look like to you?" before the work starts. One sentence can save a lot of back and forth. 2. When will they actually look at it? Sending something doesn't mean it gets reviewed immediately. It lands in a queue like everything else. What helps: Naming a review date alongside the delivery date. "I'll share this Tuesday — could you take a look by Friday?" Turns silence from a mystery into a signal. 3. What needs to be perfect vs what can be improved later? Not everything in the feedback is the same weight. A label change and a broken flow are different things. Without saying so upfront, everything looks like an emergency. What helps: Two buckets agreed early. "Here's what I'll get right before it ships. Here's what I'd revisit in a follow-up." Makes the first feedback session more productive. 4. Could they see something before the full delivery? The first time someone sees your work often sets the tone. Showing one page or one flow halfway through can catch mismatches before they multiply. What helps: A mid-point check-in. "First page is ready — want to see if this matches what you had in mind?" Five minutes that can save a round of revisions. 5. Do they have the full picture? You've been living in this