Kademlia: Algo That Turned XOR Distance Into a Network
Hello, I'm Maneshwar. I'm building git-lrc, a Micro AI code reviewer that runs on every commit. It is...
找到 2721 篇相关文章
Hello, I'm Maneshwar. I'm building git-lrc, a Micro AI code reviewer that runs on every commit. It is...
Your backend framework will outlive your current team. Choose one that the next team can still navigate — here's why .NET has been that framework for Netflix, GitHub, and Stack Overflow for over two decades. Summary Twenty-three years. That's how long .NET has been running in production. Most frameworks from that era got abandoned, forked beyond recognition, or replaced entirely — .NET kept showing up. Netflix still uses it. GitHub uses it. Stack Overflow, which has probably saved more developer careers than any single resource on the internet, runs on ASP.NET. None of these teams are using it out of inertia. They're using it because it works under conditions that expose every weakness in a poorly designed system. This article gets into how .NET actually works, what it gives teams day-to-day, and whether it makes sense for what you're building now. Key Takeaways: One codebase, five platforms — Windows, macOS, Linux, Android, iOS. No rewrites, no platform-specific forks. Three languages, one project — C#, F#, and Visual Basic coexist without forcing a rewrite. The performance tooling ships with it — JIT compiler, AOT compiler, CLR memory management, Garbage Collector. All out of the box. Why Is .NET Still Around? Honestly, this question is worth sitting with for a second — because in software, most things don't survive twenty years. They solve the problem of the moment, get widely adopted before anyone finds the sharp edges, and then get quietly replaced when something newer comes along and the migration pain seems worth it. .NET didn't go that way. Some of that is Microsoft backing — resources, long-term support commitments, a developer community that doesn't dissolve when priorities shift. But backing alone doesn't explain it. Plenty of well-resourced frameworks have died. What actually kept .NET alive is that the foundational architecture held up. The cross-platform capability wasn't duct-taped on in 2020 because everyone suddenly cared about Linux. It was in the
Hey DEV community! 👋 When writing technical documentation, user guides, or long-form informational articles, maintaining a clear and engaging reading style is highly important. However, as writers, we often fall into repetitive phrasing habits without realizing it. Traditional word counters only track isolated, single words. To evaluate multi-word phrases and understand the flow of our writing, we need a different approach. This is where an N-gram analysis becomes highly useful. To provide a safe and private solution for content editors, I built a lightweight, entirely client-side N-gram Analyzer . In this post, we will explore the technical implementation of this utility, how to handle text segmentation in JavaScript, and why local browser processing is a reliable choice for data privacy. What is an N-gram? In computational linguistics and text processing, an N-gram is a contiguous sequence of $n$ items (usually words) from a given sample of text. A Unigram represents single words ($n=1$). A Bigram represents two-word phrases ($n=2$). A Trigram represents three-word phrases ($n=3$). A 4-gram represents four-word phrases ($n=4$). Analyzing these combinations helps developers and content creators identify repetitive phrases, evaluate vocabulary diversity, and check if the thematic distribution of a document aligns with its target focus. The Client-Side Approach: Privacy and Data Isolation Many online text tools process user inputs on backend servers. This setup introduces a significant privacy risk if you are analyzing sensitive internal documentation, unpublished drafts, or proprietary code comments. By executing the lexical parsing entirely within the user's browser, we keep the processing local. The text never travels across the network, and there are no external database logs. The local device handles the entire operation. Implementing the N-gram Extraction in JavaScript Let's look at the core logic. To build an N-gram extractor, the utility must perform three ke
If you're building or evaluating an AI agent that can shop and check out on its own, you've probably run into the new "agentic commerce" protocols: ACP (OpenAI + Stripe + Meta), AP2 (Google), and UCP. They define how an agent talks to a merchant to create a checkout session, apply a payment token, and get an order back. Stripe's own test mode covers the payment half fine — test cards, test API keys. But there's no hosted "fake merchant" you can point your agent at to verify the protocol half: does your agent correctly create a session, handle a 422 idempotency conflict, parse the order response, retry politely? You either mock it yourself from the spec, or risk finding out against a real merchant. So I built acp-sandbox — a small hosted mock merchant implementing the ACP checkout API, live at https://acp-sandbox.flo-voice1.com . What it does It implements the real checkout_sessions lifecycle from ACP's 2026-04-17 spec : create, retrieve, update, complete, cancel. Responses match the actual CheckoutSession / Order / Error schemas for the fields it supports — I pulled the OpenAPI spec directly rather than guessing field names. # get a test key, no signup curl -X POST https://acp-sandbox.flo-voice1.com/keys \ -H "Content-Type: application/json" -d '{"email":"you@example.com"}' # create a session against the demo catalog curl -X POST https://acp-sandbox.flo-voice1.com/checkout_sessions \ -H "Authorization: Bearer acps_test_..." \ -H "Content-Type: application/json" \ -d '{"line_items":[{"id":"item_demo_headphones","quantity":1}],"currency":"usd"}' Every request/response is logged per API key ( GET /logs ), so you can see exactly what your agent sent when something doesn't work. What it deliberately doesn't do (yet) No real payment processing — complete always succeeds once you send any payment_data . No OAuth delegate_authentication flow. No fulfillment options (shipping/pickup) — every session goes straight to ready_for_payment . Fixed demo catalog (4 items), not a rea
This isn't my main piece for the week, it's more of a "contributes nothing to knowledge" kind of post. Last week I took another look at my portfolio and thought, "Hey, why not make this feel a bit more like me?" So I set out to give it a makeover, stuffed as much of my personality into it as I could, and et voilà, done. The old one was kinda too formal. TL;DR: I gave my portfolio a personality transplant. If you'd rather just look than read: a-thedeveloper.vercel.app Vibe / Tone Option By default, the professional option is enabled. But if you're not too sensitive and want to have a little fun, try toggling over to the unfiltered version of me, lol. I don't actually talk like that in real life anymore, but having grown up speaking English, that's pretty much how I sounded back in my teenage years. I was a grumpy teenager like everyone else, the difference is I was extra grumpy compared to most. 😭 I also lost access to my Instagram account, so all of it is still sitting there, public, for anyone to see. Every day I hope that account just quietly gets deleted. And if you're wondering whether that same energy has been erased, nope, it's still very much here. I just keep it contained to appropriate contexts now, lol. I also found these while digging through my old microsoft drive, weird 16 year old me stuff. I actually said this in a debate, by the way. Can't remember if my team won that one or lost. Weather Options Kinda irrelevant to how it actually describes my portfolio, but I initially wanted to make rainy the only option, because I'm a big fan of dark, gloomy, cloudy weather — the kind that makes England look like heaven to me. 😭 Then I thought, why not just have all of them? So now each weather option comes with its own falling elements based on the selection, plus music that I feel fits the atmosphere. Again, it doesn't really serve any practical purpose, but I think it's a nice little touch to have, haha. DEV Writing Views with an API Key When I joined DEV in 2
Over the last while I've built integrations for three places where people work with text and images: Obsidian , VS Code, and Figma. Doing a few of them back to back, I noticed something you don't see from a single one. They're all desktop apps. For your integration to exist at all, the person first installs a program on their machine, and then, inside it, your plugin. You're not writing for the web. You're writing code locked inside someone else's app — and each app has its own runtime, its own rules, and its own wall for you to walk into. The web trained us to think an HTTP request is one line. Inside someone else's sandbox, it turns out even that has to be earned. Figma was the strictest host of the three. I'll tell it through Figma, because it's locked down tighter than Obsidian or VS Code, and everything shows up on it at once. The task was almost comically simple: select a frame, write a caption, pick your social accounts, publish — without exporting the image and opening a second app. We already had the publishing API, so I expected the Figma side to be small. And it was: the main plugin file is 120 lines. The work wasn't in them. It was around them. Figma gives you bytes, not a file The first version came together easily. When the selection changes, the plugin checks whether there's one exportable node and tells the UI what it found. For the preview it exports a small copy; for publishing, separately, at 2×. const bytes = await nodes [ 0 ]. exportAsync ({ format : " PNG " , constraint : { type : " SCALE " , value : 2 }, }); 2× because the image still has a journey ahead of it: social networks recompress what you upload, and small text on a design goes noticeably softer by the time it lands in a feed. Then the first quirk of the foreign house. Figma hands the plugin not a file but raw PNG bytes — exportAsync() returns a Uint8Array . Our normal API won't eat that — it doesn't take a giant image stuffed into a JSON body. It creates a post first, hands the client
Hey DEV community! 👋 As developers, we handle Base64 encoding and decoding almost daily—whether we're debugging API payloads, formatting authorization headers, or embedding small graphic assets directly into stylesheets. However, many online translation utilities process your inputs on their backend servers. If you are dealing with sensitive configuration parameters, internal logs, or keys, pasting that data into a third-party web tool is a clear data privacy risk. To solve this, I decided to "vibe-code" a lightweight, strictly browser-based, privacy-oriented Base64 Encoder & Decoder . In this post, we will look at how this utility was built using AI assistance and vanilla JavaScript, along with the core logic to handle common encoding pitfalls. What is "Vibe Coding"? For those unfamiliar with the term, vibe coding is the practice of leveraging modern generative AI models to handle the bulk of the standard layout and event listeners, while you focus on the core logic, user experience, and privacy requirements. Instead of writing every CSS class and event listener manually, I guided an AI assistant to generate a clean, responsive layout using a standard grid framework, while ensuring that the core translation logic resides strictly in the user's browser. The Pitfall of Traditional JS Base64 (and How to Fix It) If you have ever used native JavaScript btoa() and atob() functions, you might know they struggle with Unicode/UTF-8 characters (like emojis or non-Latin scripts). Running this in your console will throw an error: btoa ( " Xin chào! 🚀 " ); // Throws "Uncaught DOMException" To resolve this during the development process, the utility implements modern TextEncoder and TextDecoder APIs. This approach converts strings into binary byte arrays before encoding them, avoiding exceptions. The Client-Side Implementation Here is the clean JavaScript snippet used for bidirectional encoding and decoding: function processBase64 ( action , inputValue ) { try { if ( action ===
There is a habit I have for other people's libraries that I do not have for my own code: before I call something, I read what it returns. With my own functions I skip that, because I wrote them, so I know. Three times in three days that turned out to be false, and the third time I caught it before it cost anything only because I had started treating my own modules like somebody else's. The version I had already been burned by twice I maintain qbofile , a set of browser-based converters between the file formats accounting software uses. It is a small codebase: a parser per input format, a generator per output format, and pages that wire one to the other. Wiring a new pair felt like plumbing, so I estimated it like plumbing. Two new pages, both reusing an existing parser and an existing generator: no new code. I said that out loud before opening either end. The generator had no column for the thing the parser produced. The parser could read the category a user had assigned to each transaction; the CSV generator emitted six fixed columns and category was not one of them. Not a bug — it had simply never needed one, because the format it was originally written for does not carry categories. That is a strange kind of wrong. Nothing was broken. The code did exactly what it always had. My model of it was built from the function name. The same evening, in the same pair of modules, the second one: L . push ( `P ${ sanitizeText ( tx . description )} ` ); P is the payee field in that output format. M is the memo. Two fields, and upstream, description was defined as memo || payee . So for any transaction that had a memo, the memo took the payee slot and the actual payee was dropped. Silently — the file is valid, it imports fine, and the missing name never announces itself. The two minutes that caught the third one After the second one I wrote down a rule and did not really believe I needed it: before wiring two components together, open both ends and read what actually crosses.
I built Unicode Text Tools , a free site with a bunch of text converters — superscript, subscript, bubble/circled text, upside-down text, small caps, and more. Type something, get it transformed, copy it out. The whole engine is one dependency-free JS file built entirely from character mapping tables . No AI, no server, no libraries. Here's why that's the right architecture for this class of tool, and how the trickier conversions work. The core idea: it's all just lookup tables Every conversion on the site is a function that maps each input character to a Unicode character (or does a small transform). The simplest cases are pure dictionaries: // Superscript (full a-z, 0-9) var SUP = { a : ' ᵃ ' , b : ' ᵇ ' , c : ' ᶜ ' , d : ' ᵈ ' , e : ' ᵉ ' , f : ' ᶠ ' , g : ' ᵍ ' , h : ' ʰ ' , i : ' ⁱ ' , j : ' ʲ ' , k : ' ᵏ ' , l : ' ˡ ' , m : ' ᵐ ' , n : ' ⁿ ' , o : ' ᵒ ' , p : ' ᵖ ' , q : ' ᵠ ' , r : ' ʳ ' , s : ' ˢ ' , t : ' ᵗ ' , u : ' ᵘ ' , v : ' ᵛ ' , w : ' ʷ ' , x : ' ˣ ' , y : ' ʸ ' , z : ' ᶻ ' , ' 0 ' : ' ⁰ ' , ' 1 ' : ' ¹ ' , ' 2 ' : ' ² ' , ' 3 ' : ' ³ ' , ' 4 ' : ' ⁴ ' , ' 5 ' : ' ⁵ ' , ' 6 ' : ' ⁶ ' , ' 7 ' : ' ⁷ ' , ' 8 ' : ' ⁸ ' , ' 9 ' : ' ⁹ ' , ' + ' : ' ⁺ ' , ' - ' : ' ⁻ ' , ' = ' : ' ⁼ ' , ' ( ' : ' ⁽ ' , ' ) ' : ' ⁾ ' }; The transform itself is trivial — walk the string, look up each char, append the mapped value (or the original char if unmapped). The work is in the tables: knowing which Unicode blocks exist, what's 1:1 reversible, and what's incomplete. The Unicode reality check Here's the thing nobody tells you about Unicode text transformation: the blocks are inconsistent. Superscript : complete for a-z and 0-9 — fully reversible. Subscript : incomplete — there's no subscript b , c , d , f , g , q , w , y , z . If you map an input with those letters, you have to decide what to do with them. Small caps : x has no small-cap form ( ꞯ is the closest, but it's a different character and looks wrong). j is a problem too — the Unicode small-cap ᴊ collides visually
Have you ever been coding and then gotten into that flow state? You know where hours pass by , and it feels to you it's only ben a few minutes? Me too. One thing that really helps me get into that state is music. So I create my own music Lo-Fi server called compile and chill. As a part of this project, I created three radio stations. Each station can generate a 16:9 scene with Amazon Bedrock , compose an instrumental loop with ElevenLabs, and turn an illustration into a six-second video through OpenRouter. Generated files live in private Amazon S3 storage and return to the browser through the Nuxt server. I also added a Stream Deck API interface! This tutorial shows how to build this radio station from start to finish. The complete source code is available in the Compile & Chill repository . Watch the full video on YouTube . Prerequisites You need the following tools for the complete build: Node.js 22.19 or newer. The locked Nuxt 4.5.2 release requires Node 22.19+, 24.11+, or 26+. npm 10 or newer. An AWS account and a configured AWS Command Line Interface (AWS CLI) profile. The AWS Serverless Application Model (AWS SAM) CLI for the private storage stack. Access to Stability AI Stable Image Ultra through Amazon Bedrock in us-west-2 . An ElevenLabs API key for music generation. An OpenRouter API key for animated scenes. The provider credentials are optional. Without them, the UI, bundled scene, station switching, player, and Focus Block timer still work. The identity running the app needs bedrock:InvokeModel plus bucket-scoped permissions for s3:GetObject , s3:PutObject , s3:DeleteObject , s3:DeleteObjectVersion , and s3:ListBucketVersions . Use a role or profile scoped to the station bucket rather than an administrator identity. For this project I included infrastructure as code with SAM to help setup the AWS parts. It's also included in the repo. Steps 1. Run the station without credentials Pull down the repo and get started! git clone https://github.com/ErikCH/comp
When someone asks whether to use Azure OpenAI Service or the direct OpenAI API, the starting point is this: the models running on both platforms are identical. GPT-4o, GPT-5, and the o-series models you deploy on Azure have the same weights, the same capabilities, and the same output quality as the ones you call from platform.openai.com, and what changes between the two platforms is the infrastructure where they run, the authentication mechanism, and the compliance guarantees the provider can offer on those requests. What changed in 2026 Azure AI Foundry was renamed Microsoft Foundry on January 1, 2026, and Azure OpenAI Service now lives inside that unified platform alongside the model catalog, development tooling, and agents. References to Microsoft Foundry in new documentation point to what used to be Azure AI Foundry. In July 2026, the GPT-5.6 family arrived with Sol, Terra, and Luna available on Azure the same day as on the direct OpenAI API. Historically Azure lagged four to eight weeks behind new model releases because Microsoft validates them within their compliance frameworks before making them available, and while that gap still exists for some specific features and APIs, for the main models in the GPT-5 family availability is converging. Where data is processed When you call GPT-4o from the OpenAI API, the request goes to OpenAI's own infrastructure, which is centralized and gives you no control over which region processes your data. For most use cases that doesn't matter, but for organizations with data residency requirements, regulatory compliance needs, or industries like healthcare, banking, or government, that detail can determine whether the service is usable at all. Azure OpenAI runs the same models within the boundary of your Azure tenant, so the data you send in prompts doesn't leave to OpenAI's infrastructure but processes in the Azure regions you choose. That's what makes it possible to meet HIPAA, SOC 2, EU data residency, and other certificati
📝 Originally published (in Japanese) at forge.workstyle.tech . When building voice-based AI interactions in the browser (avatars, voice bots, streaming AI), you’ll inevitably hit pitfalls stemming from audio physics and browser implementation quirks. This article compiles 16 traps I encountered during product development , organized in a symptom → cause → solution lookup format . No need to read from top to bottom—jump straight to the symptom you’re facing. Echo and Self-Response Issues 1. Avatar Responds to Its Own Voice (Despite echoCancellation: true ) Symptom : TTS audio is picked up by the mic, and STT recognizes it as user speech, creating a self-response loop. Cause : AEC (Acoustic Echo Cancellation) requires a reference signal (the "sound to cancel"). Only the browser's official playback paths ( <audio> / WebRTC receiver tracks) serve as references. Custom playback via Web Audio API does not reliably function as a reference . Solution : Return TTS audio from the server as a WebRTC remote track and play it via an <audio> element. This eliminates echoes without text-matching workarounds (tested: 99 seconds of continuous speech with speakers on, zero false user turn detections). 2. Echoes Are Gone, but Speaking Simultaneously with the Avatar Distorts My Voice and Causes Misrecognition Symptom : Only during dual speech, proper nouns get mangled (e.g., "社員数" → "シャインズ"), especially at word beginnings. Cause : Fundamental AEC trade-off. To cancel echoes, AEC suppresses/distorts near-end (user) audio during dual speech. Solution : Mitigate in three layers: ① Increase mic Opus bitrate and enable FEC (see Pitfall 13) ② Provide vocabulary hints to STT (see separate article: use "recent avatar speech" as initial_prompt , not a dictionary) ③ Instruct LLM: "Input is STT transcription with potential errors. Interpret unnatural words as phonetically similar terms and add confirmation prompts." 3. Can’t Suppress Audio from Other Apps (Music, Videos) Symptom : Audio/lyrics fr
Following the discussion on named AI personas and trust — here's the engineering side: how do you keep AI-status disclosure genuinely persistent throughout a conversation without making the interface feel robotic or constantly interrupting the experience a named persona is meant to create? The Naive Approaches Both Fail Option A: One disclaimer, message one, never again. Trivially easy to implement, but gets forgotten within a few exchanges — exactly the failure mode worth avoiding for personas carrying real emotional weight. Option B: Repeat "I am an AI" every single message. Technically persistent, but breaks the actual UX a named persona is trying to create, and users will tune it out as noise within a few messages anyway — repetition without variation loses its signal value fast. Neither is a good engineering solution. The better pattern is contextual, adaptive disclosure. Pattern: Risk-Weighted Disclosure Frequency python class DisclosureManager: def init (self, base_interval=8, high_risk_interval=3): self.base_interval = base_interval self.high_risk_interval = high_risk_interval self.messages_since_disclosure = 0 def should_inject_disclosure(self, message_risk_level: str) -> bool: interval = ( self.high_risk_interval if message_risk_level == "high" else self.base_interval ) self.messages_since_disclosure += 1 if self.messages_since_disclosure >= interval: self.messages_since_disclosure = 0 return True return False message_risk_level comes from the same classification pass used for scope/escalation detection covered in earlier persona-guardrail architecture — emotionally sensitive or high-stakes exchanges trigger disclosure more frequently than routine ones. Pattern: Disclosure Woven Into Persona Voice, Not Bolted On Rather than an interrupting system message, integrate the reminder into the persona's actual response style: python def inject_natural_disclosure(response_text, persona_config): disclosure_phrases = persona_config.disclosure_variants # e.g. for "Ок
I want to describe a completely ordinary hour from my week, because I suspect it's your week too. ...
I want to be upfront about something. I didn't figure this out proactively. I figured it out after my second burnout in three years — sitting in a period of forced recovery, unable to look at a code editor without feeling a specific kind of dread that I couldn't logic my way out of. I'd done everything the burnout recovery advice said to do. Took time off. Set better boundaries at the new job. Worked on the psychological stuff. All of it helped. None of it explained why recovery felt so much harder and slower than it should. Then I got bloodwork done. And the picture became considerably less mysterious. The Diagnostic Output bash $ bloodwork --full-micronutrient-panel --date=recovery-period [CRITICAL] vitamin-d: 18 ng/mL target: 40-60 ng/mL status: severely deficient duration: estimated 2+ years note: dopamine synthesis impaired at this level [CRITICAL] rbc-magnesium: low note: serum looked normal — wrong metric duration: unknown — never previously tested correctly note: HPA axis running unregulated [HIGH] omega3-index: 3.1% target: 8%+ status: neuroinflammation elevated note: western diet + zero supplementation [HIGH] hs-crp: 2.9 mg/L target: <1.0 mg/L status: significant systemic inflammation note: never measured, thoroughly normalized [WARNING] ferritin: low-normal note: passing standard panel, causing fatigue bugs-found: 5 bugs-known: 0 recovery-speed: severely impaired by all of the above Two burnouts. Same underlying biology. Neither time did anyone suggest checking any of these markers. What the Numbers Actually Meant Vitamin D at 18 ng/mL: Vitamin D is a direct input to dopamine synthesis. The enzyme that produces dopamine requires it. I had been trying to rebuild motivation and find meaning in work — the core challenge of burnout recovery — while running a dopamine system without adequate substrate. javascript // what I was trying to do dopamine.rebuild() // what the system had to work with vitaminD: 18 // severely deficient tyrosineHydroxylase.efficiency:
When you're building an AI evaluation platform with multiple microservices, the "core" services get all the attention — the evaluation engine, the scoring system, the RAG pipeline. But a platform doesn't work without the connective tissue: the workflow orchestration that keeps humans in the loop, the taxonomy engine that classifies tasks intelligently, the platform service that ties authentication together, and the evaluation suites that ensure models actually remember context. These four services don't make headlines, but they're what turned a collection of microservices into an actual platform. Here's what went into each one and why the engineering decisions mattered. Workflow Orchestration: The Human-in-the-Loop Engine AI evaluation is not fully automated — and it shouldn't be. Certain decisions require human judgment: Is this model response harmful? Does this evaluation rubric make sense for this domain? Is this edge case a genuine failure or acceptable behavior? The workflow orchestrator manages these decision points. It coordinates multi-step evaluation workflows where some steps are automated (LLM scoring, data validation) and others require human approval before the pipeline continues. The Architecture The core is a state machine built on FastAPI and PostgreSQL. Each workflow is a DAG (directed acyclic graph) of tasks, where each node can be: Automated: Runs immediately, calls another service (scoring, data enrichment), stores the result Human gate: Pauses the workflow, notifies the assigned reviewer via the notification service, waits for approval/rejection Conditional: Routes to different branches based on previous step outcomes (e.g., if confidence score < threshold, escalate to senior reviewer) State transitions are persisted in PostgreSQL with Alembic-managed migrations. Every transition is logged — who approved what, when, and with what context. This audit trail turned out to be critical for client reporting. Real-Time Updates with WebSocket The origin
Um MVP não precisa nascer preparado para milhões de usuários. Mas também não deve ser construído de uma forma que torne cada evolução futura mais cara do que a anterior. O desafio técnico de um MVP é encontrar um equilíbrio: entregar rápido o suficiente para validar hipóteses, mantendo uma base simples, observável e segura. O objetivo não é antecipar todos os cenários. É evitar decisões que bloqueiem o aprendizado. Antes da primeira linha de código, estas sete decisões reduzem boa parte do retrabalho que aparece depois do lançamento. 1. Qual hipótese o software precisa validar? “MVP” descreve uma estratégia de validação, não um tamanho de backlog. Antes de discutir framework, banco de dados ou cloud, transforme a ideia em uma hipótese testável: Acreditamos que [tipo de usuário] resolverá [problema] usando [proposta de valor]. Saberemos que isso é verdade quando [métrica observável]. Esse formato muda a conversa. Em vez de tentar reproduzir todas as funcionalidades de um produto consolidado, a equipe identifica o fluxo mínimo capaz de gerar evidência. Para um sistema de orçamento B2B, por exemplo, a hipótese inicial pode ser que compradores aceitam centralizar pedidos e fornecedores respondem dentro de determinado prazo. O MVP talvez precise de cadastro, criação de pedido, convite, resposta e comparação. Chat avançado, BI e automações podem esperar. Defina uma métrica de sucesso e uma condição de abandono. Sem isso, qualquer uso parece uma vitória e o MVP vira um projeto sem linha de chegada. 2. Onde estão os limites do domínio? A pressa costuma produzir uma base de código organizada apenas por telas ou endpoints. Funciona no começo, mas as regras de negócio rapidamente se espalham por controllers, componentes e jobs. Antes de implementar, desenhe os conceitos centrais do domínio e suas responsabilidades. Perguntas úteis: Quais entidades possuem identidade própria? Quais regras precisam ser verdadeiras em toda alteração? Que ações representam eventos de negócio? Quai
I shop on a laptop. A lamp on Amazon that costs too much. A jacket in a listing photo. Something I saw on eBay and wanted to check on Temu. On my phone, that is a camera tap. On desktop, the camera icon is mostly missing. So I built SameSame , a browser extension I still use every day. It does not send your photo to a third-party reverse-image API. It opens the visual search each store already runs - the same one their mobile apps and some out-of-stock flows use - from the page you are already on. The desktop gap Visual product search is not new. Amazon Lens, Temu camera search, AliExpress image search: they work because they search inside that store's catalog. That is different from Google Lens, which searches the open web and often returns a mix of blogs, pins, and shopping links. The catch is where those tools live. Amazon's image search is a first-class feature in the shopping app. On amazon.com in a browser, it is easy to miss or simply not there, depending on the page. Temu and AliExpress follow the same pattern: obvious on mobile, buried or absent on desktop. If you want to search by image from a laptop, the usual advice is: save the image, open the store app or a reverse-image site, upload, then repeat for the next store. That is a lot of friction for something the store already knows how to do. The searches were already there I did not invent a new matcher. Amazon, Temu, and AliExpress already run visual search against their own catalogs. On mobile that is the camera in the search bar. The same capability shows up in other places, including some out-of-stock and similar-items flows on the web. When a listing is unavailable, you have sometimes seen visually close alternatives. That is not a coincidence. The catalog search is already wired up. Desktop shopping just does not put a camera on every page. Those endpoints are not a public developer API you sign up for. They are the stores' own visual search, used by their apps and a handful of desktop pages, mostl
I recently ran into a weird issue while working on a Laravel project on Windows using Laragon . Everything was working fine until I tried to import a database through phpMyAdmin. Instead of an SQL error, phpMyAdmin simply returned: Internal Server Error The server encountered an internal error or misconfiguration... No useful message. Just HTTP 500. My SQL file was around 97 MB , so at first I thought it was probably a PHP upload limit issue. It wasn't that simple. Here is how I debugged it. 1. Check which PHP configuration is actually running From Laragon Terminal: php --ini Then I checked the important error settings: php.exe -r "echo 'error_log=' . ini_get('error_log') . PHP_EOL;" php.exe -r "echo 'log_errors=' . ini_get('log_errors') . PHP_EOL;" php.exe -r "echo 'display_errors=' . ini_get('display_errors') . PHP_EOL;" My output was: error_log=D:/C-data/laragon/tmp/php_errors.log log_errors=1 display_errors=1 One small Laragon/Git Bash issue I also found was: type php returned: php is aliased to `winpty php.exe' Because of that, commands like: php -i | grep ... sometimes returned: stdout is not a tty Using php.exe directly avoids that problem. 2. Check the PHP error log My PHP error log was: D:/C-data/laragon/tmp/php_errors.log I reproduced the import error and checked it: tail -n 50 /d/C-data/laragon/tmp/php_errors.log Nothing useful appeared. That was an important clue. 3. Make sure browser PHP and CLI PHP use the same php.ini I created a temporary file: <?php phpinfo (); Then opened it through the browser. Important values were: Server API: CGI/FastCGI PHP Version: 8.4.4 Loaded Configuration File: D:\C-data\laragon\bin\php\php-8.4.4-nts-Win32-vs17-x64\php.ini My PHP limits were already high enough: upload_max_filesize = 512M post_max_size = 512M memory_limit = 512M max_execution_time = 36000 So the 97 MB SQL file should have been allowed by PHP. 4. Check Apache logs I located the Apache error log with: grep -Ri "ErrorLog" /d/C-data/laragon/etc/apache2 /d/C-da
Most monitoring watches for something bad to appear: a 500, a timeout, an expired certificate, a slow response. A heartbeat monitor does the opposite. It watches for something good to stop appearing . Your cron runs, your backup completes, your embedded device phones home, your queue worker drains — and each of those pings a URL to say "I'm still alive." The monitor's job is to notice when the pings go quiet. That inversion is the entire value. A cron that fails throws an error you can catch. A cron that stops being scheduled — the box got reimaged, the systemd timer got disabled, the container never came back after a deploy, the account got suspended for an unrelated billing issue — throws nothing at all. There is no log line, no exception, no non-zero exit. There is only the absence of the thing that used to happen. You cannot alert on an event that does not fire. You can only alert on the silence. So heartbeat monitoring looks trivial: store a timestamp on every ping, and if now - last_seen > expected_interval , fire an alert. It is about ten lines. And it is exactly those ten lines that will let 43 days of downtime pass without a second word — because the hard part of a dead-man's switch is not detecting the death. It is staying loud after it. I know because it happened to our own. Three states, and why the third one must stay silent Start with the check itself. A naive heartbeat has two states — alive or dead — and both are wrong at the edges. The real answer set has three: alive — a beat arrived within period + grace . Everything is fine. dead — the last beat is older than period + grace . The thing stopped. Page someone. unknown — the monitor exists but has never received a single beat. That third state is where two-state heartbeat monitors self-immolate. A brand-new heartbeat you just created has no last_seen timestamp. If your rule is "alert when last_seen is too old," a null last_seen is infinitely old, so the monitor pages you the instant you create it —