今日已更新 257 条资讯 | 累计 40702 条内容
关于我们

标签:#Design

找到 308 篇相关文章

AI 资讯

The Rate Limiter Strikes Back: Designing a Token Bucket from Scratch

The Quest Begins (The "Why") I still remember the first time our API started choking under a sudden traffic spike. It was a Friday afternoon, the kind where you’re just about to log off, and the monitoring dashboard lit up like a Christmas tree. Requests were piling up, latency shot through the roof, and our users began seeing those dreaded “429 Too Many Requests” errors. We had a naive rate limiter in place—a simple fixed‑window counter that reset every minute. It worked fine when traffic was steady, but as soon as a burst hit, the counter would either let too many through (because we hadn’t hit the limit yet) or block everything for the whole minute (because we’d already exhausted the quota). It felt like trying to hold back a tsunami with a sandbag. Honestly, I was frustrated. I knew there had to be a smarter way to smooth out those bursts without penalizing honest users or over‑protecting the system. That’s when I dove into the world of rate‑limiting algorithms, and the token bucket caught my eye like a shiny loot drop in a dungeon. The Revelation (The Insight) The token bucket is deceptively simple, yet it solves the exact pain points we were experiencing. Imagine a bucket that holds a fixed number of tokens. Tokens drip into the bucket at a steady rate (say, 10 tokens per second). Each incoming request consumes a token. If the bucket is empty, the request is denied or delayed; if there’s a token, the request proceeds and the token is removed. Why does this beat the fixed‑window counter? Burst tolerance – The bucket can store up to its capacity, allowing a short burst of requests up to that limit without waiting for the next window. Smooth throttling – Because tokens are added continuously, the limiter adapts to the actual request rate rather than resetting abruptly at arbitrary intervals. Memory‑light – We only need to track two numbers: the current token count and the last time we refilled the bucket. No arrays of timestamps per key. Here’s a quick ASCII sket

2026-08-19 原文 →
AI 资讯

Your AI Agent Scheduler Needs a Clock-Skew Budget, Not Just Cron

A scheduler can be perfectly healthy and still run the wrong job at the wrong time. The failure is usually not the cron expression. It is the boundary between wall-clock time, monotonic elapsed time, leases, retries, and a process that may pause or restart. A reliable agent scheduler needs an explicit clock contract. Without one, a clock correction can make a job run twice, never run, or run after its authorization window has expired. The three clocks an agent should not conflate Use wall-clock time for human meaning and durable records: scheduled_at: when the user asked for the run not_before: the earliest acceptable dispatch time expires_at: the latest acceptable dispatch time Use a monotonic clock for elapsed-time decisions inside one process: lease renewal deadlines backoff timers watchdog intervals drain deadlines Use a database or provider sequence for ordering across processes: scheduler ownership fencing tokens attempt numbers reconciliation order A monotonic timestamp cannot be compared across hosts, and a wall-clock timestamp cannot safely measure a five-minute lease if NTP steps the clock backward. Store both kinds of evidence instead of pretending one timestamp answers every question. A small scheduling contract Here is a deliberately boring record shape: action: send_digest run_id: 01J... scheduled_at: 2026-08-19T08:00:00Z not_before: 2026-08-19T08:00:00Z expires_at: 2026-08-19T08:05:00Z lease_owner: worker-7 lease_token: 1842 attempt: 1 state: READY The important part is not the field names. It is the decision rule: The scheduler claims the run with a durable lease and fencing token. It checks wall-clock eligibility against not_before and expires_at. The worker checks that its lease token is still current before starting. The effect layer checks the token again before a side effect. If the outcome is ambiguous, record UNKNOWN and reconcile by the provider's idempotency key instead of blindly retrying. That last step matters after restarts. A clean rest

2026-08-19 原文 →
AI 资讯

Building a Location-Aware Discovery Engine: Why “Nearby” Isn't Just Distance

"Nearby" sounds like a simple feature. Calculate the distance between the user and every location. Sort by distance. Done. In practice, that's not enough. A useful local discovery engine has to understand more than geography. That's one of the problems we're tackling with LeeX. The basic version A traditional nearby query might look like: User location ↓ Calculate distance ↓ Sort ascending ↓ Return results If Restaurant A is 500 meters away and Restaurant B is 2 kilometers away, Restaurant A wins. But what if Restaurant A is permanently closed? What if Restaurant B is much more relevant to the user's category? What if Restaurant B is currently featured? What if thousands of people have recently interacted with Restaurant B? Distance alone doesn't capture usefulness. Our discovery model We're thinking about discovery as a combination of signals: Discovery Score = Distance + Relevance + Activity + Popularity + Featured status + Availability + User context The exact weighting can evolve. The important part is that proximity is one signal, not the entire algorithm. Distance still matters We don't want to ignore geography. For local discovery, distance is extremely important. A user looking for a restaurant probably cares whether it is: 500 m 1 km 2 km 5 km 10 km That's why LeeX can expose radius-based discovery. But distance should normally be combined with other information. Category context Suppose someone opens LeeX and selects: Restaurants The discovery engine should not treat every listing equally. The system already knows the user's current intent. That gives us a stronger query: Nearby + Restaurant + Open + Relevant rather than: Nearby + Everything Featured listings LeeX also has a promotion layer. Featured listings can receive additional visibility across relevant discovery surfaces. But promotional ranking needs to be handled carefully. A featured listing shouldn't necessarily make every other result useless. Instead, we can think of featured placement as an ad

2026-08-19 原文 →
AI 资讯

Design Patterns: Reusable Solutions to Recurring Problems

Design Patterns: Reusable Solutions to Recurring Problems A practical guide to classic design patterns in C#/.NET — Factory, Singleton, Repository, Strategy, and Mediator — covering what problem each one actually solves, working implementations, common .NET-specific variations, and honest guidance on when each pattern earns its complexity versus when it's unnecessary ceremony. Table of Contents Introduction Factory Pattern Singleton Pattern Repository Pattern Strategy Pattern Mediator Pattern How These Patterns Combine in Practice Patterns vs. Over-Engineering Common Pitfalls Quick Reference Table Conclusion Introduction Design patterns are named, reusable solutions to problems that recur often enough across software projects that giving them a shared name and shape is genuinely useful — not because the specific code is copy-pasteable, but because the name lets developers communicate a design intent quickly ("just make it a Strategy") instead of re-explaining the same structural idea from scratch every time. This guide covers five of the most commonly used patterns in .NET codebases, with working C# examples, and — consistent with this series' recurring theme — honest guidance on when each pattern is solving a genuine problem versus adding structure a simpler solution wouldn't need. // A pattern name compresses a whole design conversation into one word "Just inject an IPaymentStrategy and pick the implementation based on the payment method" // ← Strategy "Wrap the whole multi-step checkout process behind a single mediator call" // ← Mediator 1. Factory Pattern The problem: object creation logic that doesn't belong at the call site // ❌ The caller needs to know about every concrete shipping provider and how to construct each one IShippingProvider provider = order . Region switch { "US" => new UpsShippingProvider ( apiKey , region ), "EU" => new DhlShippingProvider ( apiKey , endpoint ), "APAC" => new FedExShippingProvider ( apiKey , credentials ), _ => throw new NotS

2026-08-18 原文 →
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

2026-08-18 原文 →
开发者

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.

2026-08-17 原文 →
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

2026-08-17 原文 →
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

2026-08-16 原文 →
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

2026-08-15 原文 →
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

2026-08-14 原文 →
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

2026-08-14 原文 →
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

2026-08-14 原文 →
开发者

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, […]

2026-08-13 原文 →
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

2026-08-13 原文 →
产品设计

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

2026-08-13 原文 →
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

2026-08-12 原文 →
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

2026-08-11 原文 →