AI 资讯
Distributed Locking in Practice: Guarantees, Failure Scenarios and Better Alternatives (2/4)
In this article, we'll explore the mechanisms to solve the coordination problem. 8. Introducing Leases To address the problem of permanent ownership, distributed systems typically replace it with temporary ownership. This concept is known as a lease . Instead of granting indefinite control over a resource, the coordination service assigns ownership for a limited period of time. Rather than stating, “You own this resource until you explicitly release it,” the system instead says, “You own this resource for the next 30 seconds.” This changes the interaction model significantly. Acquire Lease | v Execute Work | v Renew Lease | v Continue Processing As long as the application remains healthy, it periodically renews the lease to maintain ownership. If the application crashes or becomes unresponsive, it can no longer renew the lease. Once the lease duration expires, ownership is automatically revoked. At that point, another application becomes eligible to acquire the lease and continue the work. Leases solve a critical problem in distributed systems: they prevent abandoned locks from blocking progress indefinitely . The system can recover automatically without manual intervention. However, while leases improve availability, they also introduce a new class of subtle and more complex problems. Leases Depend on Time To understand the next challenge, assume the lease duration is thirty seconds. Application A successfully acquires the lease. Lease Granted Duration = 30 seconds After twenty seconds, the JVM begins a long Full Garbage Collection cycle. This pause lasts forty seconds, significantly longer than the lease duration. The timeline now becomes problematic. Lease Granted | | Processing | | GC Pause (40 sec) | | Lease Expires While Application A is paused, the lease expires. During this time, another application requests access to the same resource. The coordination service observes that the previous lease has expired and therefore grants ownership to Application B. Appl
产品设计
I Tested 10 Wireframing Tools — Here Are the Best Ones
Most designers don't lose time in the design phase; they lose it in the tool-switching phase. You...
开发者
Dark mode toggles: two states are enough
Lea's pushing back on light/dark mode implementations that display three state options for visitors: light, dark, and system. Dark mode toggles: two states are enough originally handwritten and published with love on CSS-Tricks . You should really get the newsletter as well.
AI 资讯
Cloudflare Turns CI Pipelines into TypeScript Workflows
Cloudflare has released cloudflare/ci, a CI SDK that defines pipelines in TypeScript on top of Cloudflare Workflows, giving each step durable retries and replay, concurrent steps by default and Sandbox snapshot caching. It targets the Workers runtime and depends on Artifacts, still in private beta, so the transferable lesson is the durable-step model rather than a drop-in CI replacement. By Mark Silvester
AI 资讯
Why Every AI Startup Looks the Same
Spend an afternoon browsing new AI startups and a strange déjà vu sets in. The landing pages rhyme. There is a dark hero section, a gradient somewhere between indigo and violet, a little sparkle or star icon denoting Intelligence, a headline promising to let you “chat with” your documents or data or customers, and a demo video with the same upbeat, slightly anonymous soundtrack. You could swap the logos between fifty of these sites and almost nobody would notice. Sameness on the surface Some of this is just design fashion, and design fashions always converge. But the AI cohort has converged harder and faster than most, and the reason is worth naming: when everyone is building on top of the same handful of foundation models , the differentiation has to come from somewhere else , and branding is the cheapest lever to pull. If your product is a thin layer over a model anyone can call, you cannot differentiate on the model, so you differentiate on the gradient. When the engine is a commodity everyone rents, the paint job is the only thing left to argue about. Hence a thousand identical paint jobs. Funded by the same money, chasing the same story The uniformity runs deeper than design and architecture; it reaches into the incentives. A great many of these companies are funded by the same pools of venture capital, pitched against the same market maps, and steered toward the same narrative arc — explosive growth now, monetisation later, an acquisition or an IPO at the end. When the funding, the advice and the definition of success are shared, the strategies converge. Everyone chases the same enterprise customers, adopts the same land-grab pricing, and races the same clock, because that is the shape of company the money was betting on. This produces a cohort that is not only visually and technically alike but strategically alike, which makes the whole field unusually fragile to the same shocks. A shift in model pricing, a change in what the platform providers offer natively
AI 资讯
trelix v2.11.0 to v3.1.1: Six Feature Areas, Every One of Them Off By Default
Seed three events into an audit database, then reach past the application and change one row by hand: $ sqlite3 audit.db "UPDATE audit_log SET principal='attacker' WHERE id=2" $ trelix audit verify --db audit.db Audit chain TAMPERED — first divergent entry id: 2 $ echo $? 1 Delete the newest row instead and it still catches it, naming id 3, even though the surviving rows form a perfectly valid chain. Point it at something SQLite cannot open and it exits 2 rather than 0, because "I could not check" and "I checked and it is clean" must never collapse into the same green build. None of that existed six releases ago. trelix audit verify is one command out of six feature areas that landed in trelix v3.0.0, and it is the one that most changes what the project is for. What the major bump actually is The span from v2.11.0 to v3.1.1 is six releases — v2.11.1, v2.12.0, v3.0.0, v3.0.1, v3.1.0 and v3.1.1, the last of them dated 2026-08-15 — 68 commits, 137 files changed, +19,829/-1,211 lines. v2.11.0 closed out the Jira and Linear connector work, which has its own story. Everything after it is a different kind of release. v3.0.0 carries six new feature areas: Anthropic extended thinking, a model-aware context budget, a VS Code extension that acts instead of merely displaying, a hash-chained append-only audit trail, OIDC SSO, and query-conditioned context compression. Alongside them, an opt-in FTS5 declaration boost for keyword ranking. It is a major bump because of scope, not breakage. Every one of those six is additive and off by default: TRELIX_AUDIT_ENABLED=false , TRELIX_OIDC_ENABLED=false , TRELIX_LLM_THINKING_ENABLED=false , TRELIX_RETRIEVAL_COMPRESSION=false , declaration_boost_enabled False, and context_token_budget still the exact 12_000 integer it was in v2.12.0. A default v3.0.0 install assembles context byte-identically to a default v2.12.0 install, and there is a test that proves it rather than a release note that asserts it. An audit trail you can hand to somebody
科技前沿
This Beautifully Weird Necklace Is Secretly a USB Drive
Noware’s Puff necklace whimsically reimagines flash drives, turning something functional into personalized jewelry.
AI 资讯
Token Bucket vs. Sliding Window: Building Rate Limiters That Actually Hold Under Load
Rate limiting sounds like a solved problem until you actually implement one and watch it fail in a way your load test didn't predict: legitimate bursts getting rejected, or a limiter that lets through 2x its stated limit at window boundaries. The failure modes are specific enough that it's worth working through the two dominant algorithms — token bucket and sliding window — with actual code, not just the diagrams. The problem with fixed windows The naive approach almost everyone reaches for first is a fixed window counter: pick a window size (say, 60 seconds), count requests in that window, reset the counter when the window rolls over. import time class FixedWindowLimiter : def __init__ ( self , limit : int , window_seconds : int ): self . limit = limit self . window_seconds = window_seconds self . count = 0 self . window_start = time . time () def allow ( self ) -> bool : now = time . time () if now - self . window_start >= self . window_seconds : self . window_start = now self . count = 0 if self . count < self . limit : self . count += 1 return True return False This is simple and cheap, and it's also broken in a specific, exploitable way. Say the limit is 100 requests/minute. A client can send 100 requests in the last second of window N, then another 100 in the first second of window N+1. That's 200 requests in roughly two seconds, well within the letter of "100/minute" as the code enforces it, but nowhere near the spirit of it. This is the classic boundary-burst problem, and it's the reason fixed windows get replaced once traffic is adversarial or bursty enough to find the seam. Sliding window: smoothing the boundary A sliding window log fixes this by tracking actual timestamps instead of a single counter, and counting how many fall within the trailing window at the moment of the request: from collections import deque import time class SlidingWindowLogLimiter : def __init__ ( self , limit : int , window_seconds : float ): self . limit = limit self . window_seco
AI 资讯
What Permit Files Can Teach Us About Reliable Workflow Software
Paperwork-heavy workflows rarely fail because a database cannot store another PDF. They fail because the system loses the relationship between the document, the real-world object, the decision it supports, and the stage of work it represents. Permits provide a useful example. A complete project record is not one uploaded form. It is an evidence chain that changes over time. A recent Local Service Ledger guide to Pasco County septic-repair records organizes the file into eight stages: property, existing system, site, pump-out, water and sewer, application, permit, and closeout. The guide's most important software lesson is that a receipt or contractor proposal alone does not establish the complete chain from reported problem to final recorded status. That distinction generalizes well beyond permits. 1. Give every workflow a stable subject Every document should attach to a stable entity: a property, customer, asset, case, project, or account. Do not rely on a filename or free-form address as the only identifier. Normalize enough data to prevent obvious duplication, preserve the source value, and retain a stable internal ID. For a property workflow, several records may contain slightly different owner names or address formatting. The system should help a reviewer determine whether they refer to the same site without silently overwriting those differences. 2. Separate observations, proposals, and decisions These are different kinds of facts: an owner reports a symptom; a contractor proposes a scope; an authority authorizes specific work; an inspector records a result; a final status closes the file. Collapsing them into one “project description” field destroys provenance. Model the actor, date, source, and status of each statement. The interface can display the current operational summary while preserving the earlier language that explains how the record evolved. 3. Make state transitions explicit A reliable workflow should not infer completion because a document exists
AI 资讯
To keep the AI from breaking my design, it only writes JSON. I built that out for real, and the JSON turned into code
While mass-producing web tools with an AI, I've changed how I lock the design in three stages. The previous post I wrote about that got this comment: "I'd like to see the JSON approach and the design-system approach side by side." Taken at face value, I should just put the two side by side. But first, let me add a short preface. I don't want to frame this as "the JSON approach versus the design-system approach." When I called the JSON approach a "failure" in that post, I didn't mean the method is inferior; I meant it didn't suit my particular set of tools. A page made with the JSON approach does look thin. But where that thinness comes from is easily misread. Whether the design drifts and whether it looks rich are decided separately. What stops the drift is locking the design; whether it looks rich is how much you build out. What locking with JSON removes is drift in the items you specified in the schema. Whether the screen becomes rich, on the other hand, is determined by how much you've built out the machinery that turns that JSON into a screen. So it isn't that locking with JSON is what made it look like a spreadsheet. In the previous post, too, I wrote that fattening the schema and the renderer does increase the expression itself. But that came with a caveat: past a point, it heads toward rebuilding HTML and CSS by hand. What I really want to check is one step past that. If the template sets the ceiling on expression, then building out the JSON side's template as much as the current one should produce the same screen. So what does that build-out demand? I actually built it and measured. I'll share the result, along with the JSON-approach and design-system-approach screens placed side by side under matched test conditions. I'll admit up front: at the time, I chose the design system without running this comparison. So this is me building the road I didn't take, after the fact, and measuring what that cost consists of. Same order, same one-shot So that the comparis
开发者
This is Instagram’s new logo
Instagram has unveiled a new wordmark, moving away from the recognizable cursive typeface it's used over the last decade. The updated wordmark is a strange mix of half-cursive half-print that's somehow less legible than its predecessor. Like, that totally says "Instagzam," right? "The wordmark at the top of the app hasn't changed in 10 years, […]
AI 资讯
UPI at Scale: Handling Millions of Payments
Imagine this: It's salary day. It's 2 PM. Millions of people across India suddenly open their UPI apps and start paying rent, sending money to family, paying credit-card bills, and shopping online. Now here's the system-design interview question: If millions of people make payments at almost exactly the same time, is every request hitting one central server? What prevents the entire payment system from freezing? At first glance, it sounds like a scaling problem. It isn't just a scaling problem. It's a combination of: horizontal scaling concurrency distributed systems database consistency retries idempotency backpressure failure isolation downstream bottlenecks And that's what makes payment systems such an interesting system-design problem. First: Don't Imagine One Giant UPI Server A common mental model looks like this: Millions of users | v +-------------+ | UPI Server | +-------------+ | v Bank If that were literally true, we'd have a pretty serious problem. One machine cannot safely process the country's entire payment traffic. Instead, think about a distributed system: Users | v +---------------+ | API / Gateway | +---------------+ / | \ / | \ v v v [S1] [S2] [S3] | | | +------+------+ | Payment Services | +--------+--------+ | | Bank A Bank B The exact implementation of a real payment network is much more complicated than this diagram, but this is the right system-design mental model . The important idea is: The system is distributed across many machines and participating institutions. Step 1: The First Problem — Traffic Spikes Let's take a concrete example. You want to pay your landlord: ₹25,000 At the same moment, millions of other people are doing something similar. Suddenly: Normal traffic: 100K requests/sec Salary day: ████████████████████████ 1M+ requests/sec The first question is: How do we handle the additional traffic? Naive Solution: One Powerful Server We could buy a massive machine. 1M requests/sec | v +---------------+ | HUGE SERVER | | 256 CPU core
产品设计
Font Preview Is a Classification Problem, Not a Beauty Contest
An Arabic type preview becomes more useful when it helps a designer classify intent. “Which one looks best?” is too vague. The better question is whether the phrase needs readability, geometry, ceremony, ornament, handwriting energy, or poetic flow. Six familiar script directions make that distinction visible: Naskh, Kufic, Thuluth, Diwani, Ruq'ah, and Nastaliq. They should not be treated as interchangeable decorations. Each changes the apparent density, rhythm, and purpose of the same phrase. Define the intent before rendering A preview UI can ask for a short design intent alongside the text: type PreviewIntent = | ' readable ' | ' structural ' | ' ceremonial ' | ' ornamental ' | ' informal ' | ' poetic ' The mapping is not a legal or historical verdict, but it is a practical starting point. Naskh commonly supports readable, balanced text. Kufic emphasizes geometry and strong outlines. Thuluth benefits from display scale and ceremonial space. Diwani creates dense, flowing ornament. Ruq'ah feels direct and handwritten. Nastaliq's descending rhythm is especially relevant to Persian and Urdu presentation. Preview the real phrase Specimen text hides problems. A designer should render the actual name, quotation, or headline because letter combinations alter width, joins, baseline rhythm, and negative space. The same style can feel excellent for a short name and crowded for a longer sentence. The comparison surface should keep content constant while changing one variable at a time. That makes differences attributable to the font direction rather than color, size, or wording. Respect right-to-left layout Font selection and layout cannot be separated. A preview needs explicit RTL direction, sensible alignment, and enough room for vertical and descending forms. Nastaliq in particular may need more line height than a compact Latin-oriented component assumes. .preview { direction : rtl ; text-align : right ; overflow : visible ; } This looks elementary, but many design tools
AI 资讯
Understanding Dependency Injection: From Mechanics to Architecture
For a long time, my mental model of Dependency Injection looked like this: create an interface,...
AI 资讯
Introduction to the Cloud-Native World with Azure Kubernetes Services (AKS) - Series Part 3
n today's world of cloud-native development, businesses require powerful, scalable, and flexible platforms that help developers efficiently build and operate their applications. An Internal Developer Platform (IDP) based on Azure Kubernetes Services (AKS) provides an optimized environment that brings together all the key components for modern software engineering. This article explains how to develop such a platform using AKS, what key components are required, and how to integrate them optimally. What is an Internal Developer Platform (IDP)? An internal developer platform is a set of tools, processes, and automations provided to developers to simplify the entire software development process. It offers a standardized environment where developers can write, test, and deploy code without worrying about the infrastructure or underlying complexities. An IDP built on Azure Kubernetes Services (AKS) also allows for the operation of containerized applications in a fully managed, highly available, and scalable environment. Core Components of a Development Platform on AKS When building an internal developer platform based on AKS, several key components ensure an efficient and robust system. Here are the essential elements: Azure Kubernetes Services (AKS) as the Central Platform AKS forms the core of the development platform. It provides a scalable and managed Kubernetes environment where all containerized applications run. With full integration into other Azure services, developers can access a wide range of tools to efficiently manage, monitor, and scale their workloads. Service Mesh for Managing Microservices Communication In a microservices architecture, which is commonly used in modern cloud-native applications, communication between services plays a crucial role. A Service Mesh like Istio or Linkerd enables the management and monitoring of this communication. It provides features such as load balancing, traffic management, security policies, and monitoring for microservi
AI 资讯
Dev log #16 Typographic Hierarchy and the Great Obsidian Purge
Spent the week redesigning my portfolio’s blog layout and nuking thousands of stale notes in my Obsidian vault. Between the UI polish and some deep dives into libp2p DHT de-flaking, I pushed 36 commits and managed to delete almost 16,000 lines of clutter. TL;DR I’ve always believed that your digital space needs a good pruning every now and then to stay healthy. This week was the embodiment of that philosophy. I pushed 36 commits across four primary projects, resulting in over 23,000 additions and nearly 16,000 deletions. Most of that churn came from a massive redesign of my portfolio's blog and a long-overdue "fresh start" for my Obsidian vault. On the open-source side, I spent some quality time in the weeds of py-libp2p , chasing down flaky DHT tests and proposing better subnet diversity limits. What I Built Portfolio Redesign: The Typography Pivot My main focus this week was my portfolio. I’ve been feeling like the blog layout was getting a bit cluttered, so I opened and merged PR #15, which was all about "typographic hierarchy instead of decoration." I’m moving away from unnecessary borders and boxes and letting the type do the heavy lifting. I spent a lot of time in components/blog and app/blog refining the layout. I implemented borderless filter pills and full-width rows to give the content more room to breathe. One of the bigger technical shifts was moving the blog list to be fully server-rendered. It feels snappier, and it allowed me to implement more "honest" dates and better hover states on the rows. I also added a real focus ring for accessibility (because we’ve all been frustrated by keyboard navigation that feels like a guessing game). By the time I was done, I’d touched over 200 files in that repo alone. The Obsidian Purge I also took a metaphorical chainsaw to my obsidian-vault . I nuked nearly 10,000 lines of stale content. I removed entire directories for "Projects," "Rust," and "Backend" notes that were just gathering digital dust. It’s easy to let
AI 资讯
Engineering Is the Checkable Fraction of Your Practice
Craft externalizes nothing and transfers by apprenticeship. Engineering writes the governing relation down where someone else can find it wrong. Four times now I have written the same three sentences in different notations, for four problems that looked unrelated: a design method, a coding technology, an architecture-derivation procedure, and a contract-modelling tool. I noticed the repetition only after the fourth. Here it is, stated as precisely as I can manage. Structure is derived from the attribution of forced change. The attribution is kept as an explicit, checkable artifact. The derivation refuses rather than guesses when its inputs underdetermine the answer. Three clauses, each carrying weight. Drop one and look at what remains. Drop derived and you have a documentation exercise: the structure was chosen first and the attribution written to match. This is the normal case, and it makes no prediction, so nothing can disagree with it. Drop explicit artifact and you have taste. Real, valuable, and transferable only by apprenticeship. Drop refusal and you have a generator that answers every question. Its answers carry no information, because it was always going to produce one. The third clause has a lineage worth claiming Type inference has refused for fifty years. Hindley-Milner unification fails rather than picking a plausible substitution: when two types cannot be reconciled, the answer is an error, not a guess. Core HM needs no annotations at all -- it infers principal types, and that is the point. The interesting part is what happened when later extensions broke that guarantee. Type classes admit programs whose type is inferable while the instance to use is not; GADTs and polymorphic recursion break principality outright. In each case the compilers were free to pick a plausible candidate, and they demand an annotation instead. Build systems joined later: Bazel refuses an undeclared dependency rather than resolving it from ambient state ( this rule is missing
AI 资讯
Polling vs. Webhooks vs. WebSockets vs. SSE: Choosing the Right Real-Time Architecture
API design patterns API event architecture API integration strategies API latency comparison API performance optimization API resource efficiency asynchronous API architecture automated API triggers backend architecture bidirectional API communication developer guide API event architecture event driven API design event driven architecture event driven webhooks HTTP long polling vs webhooks HTTP polling vs websockets HTTP request response vs sockets InstaWebhook microservices event communication polling overhead polling vs webhooks polling vs websockets publish subscribe architecture pub sub vs webhooks real time API integration real time communication protocols real time data streaming protocols real time notification architecture real time web applications REST API vs webhooks REST API vs websockets scalable API architecture server push technology server sent events vs webhooks short polling vs long polling socket connection vs webhooks software engineering API design webhook architecture webhook delivery system webhook infrastructure webhook listener webhook payload delivery webhooks best practices webhooks vs sockets vs polling comparison webhooks vs websockets websocket architecture websocket client server architecture websocket full duplex web sockets vs long polling when to use API polling when to use webhooks when to use websockets Polling Vs Webhooks Vs Web Sockets Vs SSE Choosing The Right Real Time Architecture Polling vs. Webhooks vs. WebSockets vs. SSE: Choosing the Right Real-Time Architecture Choosing how your systems communicate state changes is one of the most consequential decisions in API design. Whether you're building a notification engine, integrating a payment gateway, or streaming an LLM response token-by-token, the communication pattern you pick determines your app's latency, your infrastructure bill, and how much operational complexity you sign up for. Client-server systems started with a simple request-response loop: the client asks, the se
AI 资讯
Architectural Foundation: The Host-Guest Split
A compiled application cannot hot-reload itself if its main loop, window context, and memory allocations live inside the binary being recompiled. The application must be split into two layers:Host Shell (Stable Execution Root):Statically compiled once.Manages the OS window, render loop, event polling, network sockets, and high-level heap allocations.Exposes a dynamic symbol loader (dlopen / LoadLibrary or a dynamic WebAssembly runtime execution context).Guest Module (Hot-Swappable Logic):Compiled as a shared dynamic library (.so, .dylib, .dll) or an isolated WebAssembly (.wasm) module.Contains frame updates, business rules, rendering instructions, and component tree logic.Exports explicit interface hooks (init, update, render, pre_reload, post_reload).The Hot-Reload PipelineWhen a developer edits source code in a compiled language (e.g., modifying a Rust UI render function or a C# algorithm), the dev server orchestrates a zero-downtime swap through this explicit pipeline:1.File Watcher & Fast Incremental Compile:Sub-second artifact generation.The watcher detects source changes and invokes an incremental compilation pass using dynamic linking configurations (e.g., -rdynamic, dynamic C-runtime links, or fast lld/mold linkers) to output a versioned binary artifact (logic_v2.so).2.Live Manifest Update:Atomic state & symbol mapping emit.The dev server emits an updated JSON manifest containing module hash, exposed symbol tables, binary payload locations, and updated asset hashes over a WebSocket/IPC stream to the Host Shell.3.State Snapshot & Freeze:Preserving user context.The Host Shell signals pre_reload() to the currently loaded logic_v1.so. The guest logic serializes volatile runtime state into a host-managed memory buffer or leaves pointers active inside a host arena.4.Dynamic Unload & Library Swap:Operating system symbol rotation.The Host Shell unloads logic_v1.so (releasing file locks via temporary copy paths on OS platforms like Windows), loads logic_v2.so, and re
AI 资讯
Buildpacks Move the Container Hardening Control Point Away From the Dockerfile
Cloud Native Buildpacks, which graduated within the CNCF in July 2026, move base image choice out of per-service Dockerfiles into a single builder owned by platform engineering, enabling fleet-wide patching. BellSoft's hardened Paketo builder is the latest sign that vendors now treat the builder, not the Dockerfile, as the container security control point. By Mark Silvester