URL Shortener & Link Management Service

Architecture Views

23 views, in reading order. Every view ships three ways: an HTML page, an SVG that re-opens in diagrams.net fully editable, and draw.io source.

The service behind every short link anyone has ever tapped without thinking: a code goes in, a redirect comes out, 150,000 times a second, and the destination can still be changed after the code is printed on ten thousand cards. Read the set in order — the boundary first, then the people, then the structure that serves them. One decision runs through all twenty-three views: the redirect plane and the management plane are separate systems joined only by one-way propagation, and the redirect plane never writes.

Context and scope

What sits inside the boundary, who is on the other side of it, and the four things this platform refuses to own.

People and journeys

Who the platform is for, and what each of them gets to do — including the anonymous clicker who never signed up for anything and is the only actor at full traffic scale.
03 The people who click Link clicker never signed up for this Goal — I clicked a link somebody sent me. I want the page, now, and I want to know if it is a trap. Core journeys Click a short link the only journey at 150k/s Preview before clicking The people who own links Campaign owner 40k tenants Goal — The code goes on 10,000 printed cards on Friday. I need to be able to change where it points afterwards. Core journeys Launch a campaign link alias + branded domain Kill a live link Read the click report Customer developer bulk and API users Goal — Shorten 40,000 links in one import and get back a result per row, not a 429. Core journeys Bulk import a campaign Subscribe to the click stream Tenant administrator enterprise tier Goal — Prove our branded domain is ours, and know before its certificate expires, not after. Core journeys Verify a branded domain Scope and revoke API keys The people who defend it Trust and safety analyst 24/7 rota Goal — Stop a phishing link within a minute of hearing about it, and be able to show why I did. Core journeys Block and appeal 60 s globally Work the review queue Platform SRE on call Goal — Keep the redirect path up while everything behind it is broken, and know which one it is. Core journeys Absorb a viral link Fail a region out Rebuild the projection Machines in the cast Chat unfurler ~18% of traffic Goal — Fetch the link so my user sees a preview. I am not a click and must not be counted as one. Core journeys Prefetch a destination Reputation feed 3 feeds Goal — Tell the platform a destination turned malicious after it was approved. Core journeys Deliver a verdict change Who the Platform Is For, and What They Get to Do Person or role Journey / task External / third party The clicker never signs up, never logs in and is the only actor at full traffic scale — which is why the redirect path owes them a working link, not a branded one. v 1.0 · owner Platform Architecture · date 2026-09 Actors and Their Journeys Nine actors in four groups, each with a goal in their own voice — and two of them are machines that must be counted separately from humans. HTML page SVG draw.io

Structure

The parts, the two planes they belong to, and the interfaces that outlive the implementation.
08 Azure Front Door Premium — global edge Request handling TLS and anycast shared + branded WAF and bot manager Redirect cache 30 s TTL Telemetry out Access log to Event Hubs one row per click Redirect plane — read-only, 12 regions Resolution Resolver service Container Apps Deny-set matcher in-process, 5 s refresh Routing rule engine device, geo, locale Regional read cache Hot set Redis · 97% hit Negative cache unknown codes Management plane — 2 regions, one write region Link lifecycle Link API create, edit, kill Code allocator random base62-7 Bulk importer 10k per request Tenancy and trust Domain and TLS service Screening worker Functions Review and appeal Propagation Projection publisher change feed Revocation publisher Event Grid State Authoritative Link table Cosmos DB · TTL Reservation index strong, single writer Derived and evidential Click store Data Explorer Audit log immutable Reputation feeds 3 providers Microsoft Entra ID miss write verdicts clicks Container View — Two Planes, One Link Table Interface / broker Security / platform Data store Queue / topic Application we own External / third party synchronous event / async failure / alternate Omitted for clarity: the console, the click export API, the alias claim (view 15) and the cold read from the link table (view 09). Dashed amber is propagation, dashed red is a block; the redirect plane never writes to State. v 1.0 · owner Platform Architecture · date 2026-09 Container View — Two Planes The C4 level-2 view, drawn so the plane boundary is the most visible thing on the page. HTML page SVG draw.io

Data

What is stored, what is authoritative, what is disposable, and how a change reaches the edge.

Runtime

What actually happens when somebody clicks, creates or kills a link — and where the platform's three published promises are kept or broken.

Operations

How this is deployed, released, watched and recovered, on a path that is never allowed to stop.
18 Azure Front Door Premium — 190+ PoPs, one anycast address set Every PoP Anycast TLS shared + 12k branded WAF and bot rules Response cache 30 s Read regions — 12, identical, stateless West Europe (example read region) Resolver · Container Apps 3 zones, KEDA on RPS Azure Cache for Redis zone-redundant Cosmos DB read replica in-region 11 further read regions Same three components no regional state of its own Write regions — North Europe (primary) and East US 2 (standby) Primary Management plane Container Apps Cosmos DB write region single writer Screening and publishers Functions Standby Management plane warm, scaled to 1 Cosmos failover target RTO ≤ 5 min for writes Shared platform services Identity and secrets Entra ID workload identity Key Vault per-region replica Telemetry and analytics Event Hubs geo-paired Azure Data Explorer 2 clusters Azure Monitor SRE and deployment GitHub Actions origin group, latency based replication ≤ 5 s access log warm and deny-set failover pair Deployment — Global Read Path, Two Write Regions Interface / broker Security / platform Data store Application we own Queue / topic Person or role synchronous event / async two-way Losing the primary write region stops link creation and editing; it does not stop a single redirect. That asymmetry is the deployment expressing the plane split. v 1.0 · owner Platform Architecture · date 2026-09 Deployment Architecture Twelve read regions that hold no state of their own, two write regions that hold all of it. HTML page SVG draw.io

Assurance

Why it is safe: the trust zones, what crosses them, and the one actor who is never asked to prove anything.

Architecture One-Pager

The problem, the shape of the answer, the decisions that carry it, and why it should still be right in ten years.

The redirect plane and the management plane are separate systems joined only by one-way propagation, and the redirect plane never writes. Everything the platform wants from a click happens after the redirect has already been sent.

A short link is the most disposable-looking artefact in software and one of the least changeable. It goes into an email, onto a poster, into a QR code on a badge, and from that moment the platform cannot reach it. What it can change is where the code points — and that is the product. So the system has to do three things that pull against each other: answer 150,000 lookups a second from everywhere on earth in single-digit milliseconds; let an owner repoint or kill a code that is already printed on ten thousand cards, and have that take effect globally in under a minute; and count every click accurately enough to bill on, without ever putting a write in front of a read that outnumbers writes 500 to 1. The obvious architecture — one service, one database, look up the code, increment the counter, redirect — satisfies all three on a whiteboard and none of them in production.

Codes are claimed once, by a conditional create against a strongly consistent reservation index, and are then immutable for the life of the platform: never reassigned, never reissued, never reclaimed. The link record is written only by the management plane, in one write region, and is the single authority for what a code means. Resolution reads a projection of it, never the record itself on the hot path: Front Door terminates TLS at the nearest of 190+ PoPs for both the shared and the 12,000 branded domains, caches the redirect for 30 seconds, and forwards a miss to the nearest of twelve stateless read regions, where a resolver checks the deny-set first, coalesces concurrent misses, then walks an in-process LRU, a regional Redis hot set and an in-region Cosmos replica. Two propagation channels cross from management into resolution, with two published bounds: a compact revocation delta that reaches every region within 60 seconds, and a full projection off the change feed within five minutes. Clicks are counted from the edge access log rather than from application code, which is what lets a redirect be cached without losing the click, and which keeps every write off the read path. Destinations are screened before a code is issued and re-screened afterwards, because a destination that was clean in March can be phishing in June.

What it is, and what it is not

A read-only redirect plane fed by one-way propagationone service that reads and writes the link table on the click path, which is correct in every steady-state diagram and makes the write store carry the read rate.
Clicks counted from the edge access loga counter incremented by the resolver, which forces every click to reach application code and makes caching and analytics mutually exclusive.
Revocation on its own fast channel with a published 60-second boundone propagation path sized for the tightest deadline, which pays the urgent cost on every routine edit.
A code that is identity, and a destination that is statea reusable code, which eventually resolves a 2024 QR code to a stranger's site.
Exactly one strongly consistent operation, and it is alias uniquenessstrong consistency on the read path, which would make a global redirect a coordination problem.
A 302 with a 30-second cache lifetimea permanent redirect, which is faster, cheaper, and makes both revocation and counting impossible for as long as a browser chooses to remember it.
Four distinct terminal states the clicker can act ona generic 404 for unknown, expired, deactivated and blocked alike, which turns every dead link into a support ticket.
Screening as a product requirementan abuse process bolted on after the first phishing campaign, by which time the domain's reputation is already the problem.

The decisions that are the architecture

01Two planes, one-way propagation

The management plane is the only writer; the redirect plane reads a projection and a deny-set and holds no write credential. A four-hour management outage costs zero redirects.

ADR-01

02Revocation has its own channel

Stop instructions travel as a compact delta with a 60-second bound, independent of the five-minute projection. Urgency is bought where it is needed and nowhere else.

ADR-02

03Codes are random, then immutable

Seven unguessable base62 characters, claimed by conditional create, never reassigned and never reissued after retirement. (ADR-04 carries the rest.)

ADR-03

04One strongly consistent operation

Alias uniqueness. Everything else on the read path is eventually consistent, with its staleness published as a number rather than implied as instant. (ADR-15 carries the rest.)

ADR-05

05302 with a 30-second cache

Slower and more expensive than a permanent redirect, and the only choice that keeps a printed link both countable and killable.

ADR-08

06The redirect path never writes

Click events come from the edge access log after the response has left, durably buffered, with a 0.05% loss budget and daily reconciliation. (ADR-11 carries the rest.)

ADR-10

07Bots are classified, not blocked

An assumed 18% of traffic is unfurlers and scanners. They are served, tagged, excluded from customer counts and from tenant quotas, and kept in the raw data.

ADR-12

08Screening is continuous, and blocking is loud

Destinations are checked before a code is issued and re-checked afterwards; a blocked link gets a platform interstitial that names the reason, never a silent 404. (ADR-14 carries the rest.)

ADR-13

09Branded domains are the platform's problem

Twelve thousand customer domains mean certificate renewal is an availability risk the platform owns and alarms on before expiry, not a customer responsibility.

ADR-16

Why this should still be right in ten years

A link platform outlives everything around it, because its output is printed, embedded and forwarded by people who will never contact it again. These are the properties that should survive a change of scale, of cloud, and of the team.

Code immutability is the promise that ages best

Every other decision here can be revisited. This one cannot, because the evidence of its violation arrives years later as a QR code on a poster resolving to a stranger's site. It depends on no technology and costs almost nothing to keep — which is exactly why a future team, looking at a table of desirable retired aliases, will be tempted to break it.

The plane boundary survives the cloud

ADR-01 says the redirect path reads a projection and never writes. That holds if Front Door is replaced by a programmable edge, if Cosmos DB is replaced by anything with a point read, and if the whole thing moves to another provider. What would break it is one feature request — real-time click counts on the redirect response — which is why the boundary is drawn in views 08 and 14 rather than left as a convention.

Published staleness is a contract, not a caveat

The 60-second revocation bound and the five-minute edit bound will both be argued about, and both may tighten as technology improves. What must not change is that the platform states a number instead of implying immediacy, because support answers, customer expectations and abuse response all derive from that number being sayable in one sentence.

Counting from the edge log is a structural choice

It is the reason caching and analytics are not in conflict, and it will still be the reason on any edge platform that produces a request log. The fragile version of this decision is counting in application code, which looks identical on a dashboard and quietly couples the cache hit rate to the accuracy of the numbers customers are billed on.

Resolution placement is the decision most likely to be redone

ADR-07 puts the lookup in twelve regional resolvers because Azure offers no general-purpose replicated key-value store at the PoP. That is a statement about 2026, not about architecture. When a programmable edge with a replicated store is available and affordable, the lookup should move — and nothing above needs to change when it does, which is the test of whether the boundary was drawn in the right place.

Non-functional targets

Every number here is a stated assumption from the requirement, chosen to be argued with. The right-hand column names the view where the mechanism that meets it is drawn.

QualityTargetHow it is metView
Redirect availability ≥ 99.99% monthly per region Anycast edge with a 30 s response cache, twelve stateless read regions, and no synchronous dependency on the management plane or the click pipeline. 18
Redirect latency p50 ≤ 8 ms, p99 ≤ 40 ms; ≤ 120 ms p99 on a cold read Edge cache, then an in-process LRU, a regional Redis hot set and an in-region Cosmos replica, with concurrent misses coalesced per code. 14
Correctness Zero codes resolving to a destination they were not assigned Conditional create against a strongly consistent reservation index, permanent code retirement, and a continuous synthetic wrong-destination probe. 15
Revocation Effective globally ≤ 30 s p95, ≤ 60 s p99 A compact revocation delta on its own channel, pulled every 5 s, matched before any cache layer; the 30 s edge TTL bounds the worst case. 16
Edit propagation ≤ 5 min p99 globally; ≤ 1 s p99 in the creating region Cosmos change feed to a projection worker in each read region, pre-warming the hot set at creation. 13
Throughput 150,000 redirects/s steady, 600,000/s peak for 5 min 97% served at the edge; resolver fleets scaled on requests per second with 4× headroom for the assumed burst. 18
Hot key A single code at 80,000/s without degrading others In-process LRU ahead of the shared cache, request coalescing, and a hot-set distribution that does not concentrate on one cache shard. 09
Creation p99 ≤ 250 ms; 10,000-link import ≤ 60 s p99 Single-region conditional create plus a durable write, with bulk import as an asynchronous job on separate capacity. 15
Analytics freshness Click visible ≤ 60 s p95, ≤ 5 min p99 Edge access log to Event Hubs to Azure Data Explorer, with rollups as materialised views. 17
Analytics accuracy Loss ≤ 0.05%; counts within ±0.5%; uniques ±2%, labelled Durable buffering at the point of collection and daily reconciliation against the edge counters. 17
Durability Link table RPO 0, read RTO ≤ 60 s, write RTO ≤ 5 min Cosmos DB multi-region replication with a single write region and a named failover target. 18
Rebuildability All derived state rebuilt ≤ 30 min, no operator input Hot set, negative cache and deny-set are all projections of the link table, rebuilt from the change feed; drilled monthly. 19
Cost ≤ $0.05 per million redirects; alarm above $0.08 Edge hit rate ≥ 97% as the primary lever, raw click retention capped at 90 days, bot traffic metered separately. 20

Scope

In scope

  • Code allocation, custom aliases, reserved-name refusal and permanent code retirement.
  • The redirect path: edge termination for shared and branded domains, resolution, routing rules, parameter merge and the four terminal states.
  • Link lifecycle: destination editing, scheduled expiry, deactivation, reactivation and the audit trail behind each.
  • Revocation and its published 60-second bound, including abuse blocking and the interstitial.
  • Branded domain verification, certificate provisioning and renewal alerting.
  • Click collection, bot classification, rollups, the export stream and the billing meter feed.
  • Destination screening at creation and periodically thereafter, the review queue and the appeal path.

Explicitly out of scope

  • Campaign management, email or SMS sending, and CRM integration. The platform publishes click data and stops there.
  • The destination page. The platform never fetches, renders, proxies or rewrites it — it names a location.
  • Link-in-bio pages, QR design and creative tooling. QR generation appears only as a Phase 3 item over the same code.
  • Customer-side attribution modelling. The platform reports clicks, not conversions.
  • Consent management, because the click record deliberately holds no identifier that would require it.

What a four-week prototype should prove

Four of this architecture's claims are cheap to test and expensive to be wrong about. A prototype that proves these can be built on; one that skips them is a demonstration of HTTP redirection, which is the easy half.

  1. The revocation promise end to end: kill a link and measure, from outside, when the last PoP stops serving it — across all regions, with the edge cache warm, repeated a thousand times and reported at p99 rather than as an average.
  2. Uniqueness under a real race: thousands of concurrent claims for the same alias across regions, asserting exactly one winner and a named conflict for everyone else, including claims against retired codes.
  3. Hot key absorption: one code taken from zero to 80,000 requests per second in thirty seconds with a cold cache, measuring both its own latency and the p99 of every other code in the same region throughout.
  4. Counting from the edge log at the full cache hit rate: 97% of clicks never reaching application code, reconciled against the edge counters to within ±0.5%, because this is the claim that makes ADR-08 and ADR-10 compatible.
  • Fail the write region mid-import and confirm creation is refused with a retryable error and the bulk job resumes without issuing a second code for any row.
  • Flush every regional hot set and deny-set at once and confirm the deny-set is restored first, so a killed link never resolves during the rebuild window.
  • Flip a reputation feed's verdict on a link with live traffic and confirm it stops resolving within 60 seconds at every PoP, with the evidence recorded and the owner able to see the reason.
  • Let a link's TTL expire while its 302 is cached at a PoP and confirm the clicker gets a 410 rather than a redirect within the published bound, and that the expiry is not discoverable early by an enumerating client.

Open risks, carried rather than hidden

RiskIf it landsResponse
The edge cache hit rate does not reach 97% Cost per million redirects rises towards the origin-read price, the resolver fleet must grow several times over, and the 40 ms p99 becomes a cold-path p99 instead. Measure the real code popularity distribution early. The designed levers are a longer TTL (which weakens the revocation bound and must be traded explicitly), a larger hot set, and pre-warming on creation.
Revocation is promised at 60 seconds and delivered at five minutes The platform's safety claim fails silently: dashboards stay green because the mechanism is healthy while the end-to-end result misses the bound. The synthetic revocation probe in view 20 is the control, alarmed on the promise rather than on component health. The 30 s edge TTL is a design parameter and any proposal to raise it must state the new bound.
A single write region constrains creation availability Regional failover makes link creation unavailable for up to five minutes, which a large customer's bulk import will eventually hit. Accepted deliberately: refusing creation is preferable to two winners for one alias. Bulk imports are jobs with resumable semantics, so a failover delays rather than fails them.
Bot classification is wrong in the direction customers notice Either campaigns are judged on preview traffic, or genuine clicks are discarded as bots — and the second is far worse because it is invisible. Classification tags and never blocks; the raw class is retained so a reclassification can be replayed over history rather than requiring the data to be re-collected.
The platform becomes phishing infrastructure The shared domain's reputation is destroyed by browser and mail-provider blocklists, which takes every legitimate tenant's links down with it. Screening before issuance, periodic re-screening, challenges on bulk creation from unverified accounts, and a revocation path fast enough that response time is measured in seconds.

Architecture Decision Record

Why every component and every technology on these 23 views is what it is, and what each choice costs.

Sixteen decisions make up this architecture. Everything else across the twenty-three views is convention, sizing or consequence. Each record states the forcing question, the context that makes it hard, what was decided, how it is realised on Microsoft Azure, the options weighed, what the choice buys and costs, the conditions that would flip it, why it should still be right in ten years, and the lesson worth carrying to a different system.

Status of this document. This is a design, not a report on a running system. The rates, latencies, volumes, retentions and windows are the requirement's stated assumptions for a mid-size commercial link platform — 40,000 tenants, 12,000 branded domains, 12 billion stored links, 150,000 redirects per second — invented to be defensible and arguable rather than absent. They are to be replaced by measured telemetry before build, and four of them in particular are the first numbers real data should overturn: the 97% edge cache hit rate, which decides the cost per million redirects and the resolver fleet size; the 4% thirty-day working set, which decides whether the hot set is affordable at all; the 30-second edge cache TTL, which is simultaneously the hot-key absorber and the outer bound on the revocation promise; and the 18% bot share, which decides whether every customer-facing number is credible.

How to read a record

QuestionThe forcing question: why a decision was needed at all.
ContextThe requirement, the scale and the constraint that make it hard.
DecisionWhat this architecture does, stated so it can be checked.
How it is realised on Google CloudThe concrete mechanism: which service or package, configured how, in which project.
Options weighedChosen, rejected, deferred, or right elsewhere, with the reason for each.
ConsequencesWhat the choice buys and what it costs, both kept visible.
Choose differently whenThe conditions that would flip the decision for your system.
Why it holds up over timeWhat keeps the decision right as scale, staff and technology change.
LessonThe principle that transfers beyond this platform.

Decision map

Planes and propagation 3

Where the writer's involvement ends, and how a change reaches a PoP on the other side of the world.

ADR-01The redirect plane reads a projection and never writes ADR-02Revocation travels on its own channel, ahead of the projection ADR-15Staleness bounds are published as contracts, not implied as immediacy

Code and identity 4

How a code is chosen, who guarantees it is unique, and why it can never be used twice.

ADR-03Codes are random, unguessable and seven characters — not counter-derived ADR-04A code is identity, and is retired permanently rather than reused ADR-05Alias uniqueness is the only strongly consistent operation ADR-06The code namespace is per domain, not global

The read path 3

Where a lookup happens, what it is allowed to depend on, and what it returns.

ADR-07Resolution happens in twelve regional resolvers, not at the PoP ADR-08A 302 with a 30-second cache lifetime, never a permanent redirect ADR-09The link table is partitioned by code hash, not by tenant

Counting 3

How a click becomes a number somebody bills on, without a write on the redirect path.

ADR-10Clicks are collected from the edge access log, after the response has left ADR-11Full click events are shipped, with sampling reserved for pathological codes ADR-12Bots are classified and served, never blocked or silently counted

Trust and tenancy 3

Screening, terminal states, and the 12,000 domains the platform serves on customers' behalf.

ADR-13Destinations are screened before issuance and re-screened afterwards ADR-14Four distinct terminal states, owned by the platform ADR-16Branded domains and their certificates are the platform's responsibility

Technology by capability

Every capability on the twenty-three views, the Azure service chosen for it, the alternative that was weighed, and the reason. Azure was chosen for the package as a whole because it has appeared once in the practice's last ten deliveries while Google Cloud and AWS have carried three each — and because the topic's hard parts land on Azure primitives without a managed product making the architectural decisions: a globally replicated point-read store with native TTL, an anycast edge with per-domain managed certificates, and a high-cardinality append-only analytics store.

Open source This design
CapabilityChoiceOriginCredible alternativeWhy this oneRecord
Edge termination and caching Azure Front Door Premium — anycast, WAF, rules engine, 30 s response cache Azure Traffic Manager plus regional gateways One anycast address set serving the shared domain and 12,000 branded domains with managed certificates, plus a response cache that carries the assumed 97% of traffic. DNS-based steering could not provide either. Also shaped by ADR-08, ADR-16. ADR-07
Branded domain certificates Front Door managed certificates with Azure DNS verification Azure Customer-supplied certificates in Key Vault At 12,000 domains, renewal is the availability risk. Managed issuance removes 12,000 expiry dates the platform would otherwise chase; customer-supplied certificates remain available for the enterprise tier that insists. ADR-16
Resolver compute Azure Container Apps, twelve regions, KEDA-scaled on requests per second Azure Azure Functions on a consumption plan Resolution is a warm, connection-heavy, latency-sensitive path with a 40 ms p99 budget; a container fleet holds that budget more predictably than per-request compute, and KEDA absorbs the assumed 4× burst. ADR-07
Link table Azure Cosmos DB — single write region, read replicas in all read regions, native TTL Azure Azure SQL with geo-replication The access pattern is a point read on a compound key at enormous cardinality, with expiry as a data property. Cosmos gives per-partition single-digit-millisecond reads, a change feed for propagation, and TTL for expiry in one component. Also shaped by ADR-09. ADR-01
Alias uniqueness Cosmos DB conditional create (If-None-Match) in the write region Azure Multi-region writes with last-write-wins A conditional create is a genuine compare-and-set. Multi-region writes would make the platform's one zero-tolerance guarantee probabilistic. ADR-05
Hot set cache Azure Cache for Redis, zone-redundant, one per read region Azure Cosmos DB integrated cache An explicit cache with measurable hit rate, eviction metrics and a pre-warm path; the integrated cache is easier and gives less control over exactly the number the cost model depends on. ADR-07
Revocation propagation Azure Event Grid topic per region, 5 s pull into an in-process set Azure The same change feed as the projection The 60-second bound needs a channel carrying only identifiers. Sharing the projection's path would couple a safety promise to the throughput of routine edits. ADR-02
Projection propagation Cosmos DB change feed into a per-region projection worker Azure Dual-write from the management plane to each region The change feed is replayable and ordered per partition, which is what makes the projection rebuildable in 30 minutes. Dual-write makes the writer responsible for twelve regions' availability. Also shaped by ADR-15. ADR-01
Click collection Front Door access logs to Azure Event Hubs, 7-day retention Azure Resolver-emitted events The log covers cached and uncached clicks alike, which is the only way a 30-second cache and an exact click count can coexist. Also shaped by ADR-11. ADR-10
Click analytics Azure Data Explorer — raw 90 days, materialised views for rollups Azure Synapse or a lakehouse High-cardinality append-only time series with interactive per-code queries is exactly ADX's shape; materialised views give rollups without a separate batch pipeline. ADR-11
Management plane Container Apps in two regions, active-standby Azure Active-active in twelve regions The write path is bound to a single write region anyway (ADR-05), so more management regions would add cost without adding availability. Also shaped by ADR-05. ADR-01
Screening and publishers Azure Functions, event-driven Azure Long-running workers on the management plane Screening and re-screening are bursty and idempotent; the periodic re-screen is a scheduled fan-out with no state of its own. ADR-13
Reputation verdicts Three third-party URL reputation feeds Third party A single provider, or in-house classification Three providers mean no single vendor outage or false-positive run decides the platform's behaviour; in-house classification is a different company's product. ADR-13
Identity Microsoft Entra ID with workload identity; tenant-scoped API keys hashed in Key Vault Azure Connection strings and a self-managed key store No credential in configuration anywhere, and key revocation without a redeployment. The resolver holds a read-only workload identity and nothing else. ADR-01
Audit and abuse evidence Append-only store with an immutability policy — 400 days and 2 years Azure Rows in the operational database An audit record that the application can update is not evidence, and an abuse decision has to be defensible after the customer has escalated. Also shaped by ADR-14. ADR-13
Observability Azure Monitor for resources, ADX for the promise probes Azure Resource monitoring alone Component health would have stayed green through every failure this platform actually fears. The wrong-destination and revocation probes are queries over ADX, alarmed on the published promise. Also shaped by ADR-05. ADR-02
Infrastructure and delivery Bicep modules with GitHub Actions and OIDC federation Azure + GitHub Terraform with a service principal secret Twelve read regions have to be identical by construction, and OIDC removes the last long-lived secret from the pipeline. ADR-01

The decisions, and the alternatives that lost

Planes and propagationWhere the writer's involvement ends, and how a change reaches a PoP on the other side of the world.

ADR-01

The redirect plane reads a projection and never writes

Accepted

Does serving a click touch the authoritative link table, or a derived copy of it that the click path cannot modify?

Context
The tutorial architecture is one service and one database: look up the code, increment a counter, return a redirect. At a 500:1 read-to-write ratio it means the write store must be provisioned for the read rate, the redirect's availability becomes the availability of the click pipeline, and a bulk import competing for the same store slows down everybody's clicks. Every softer variant has the same defect in a smaller form — a click that writes anything synchronously, even asynchronously flushed from the same process, makes a printed link's behaviour depend on the health of a system it has no reason to depend on. And the failure is not graceful: the codes people are clicking most are exactly the ones whose writes are hottest.
Decision
The link table is written only by the management plane. The redirect plane reads a projection of it — an edge response cache, a regional hot set, an in-region read replica — plus a separately propagated deny-set, and holds no credential that could write anything. A click produces no synchronous write of any kind. The redirect plane must be able to serve for at least four hours with the entire management plane unavailable, learning nothing new but losing nothing already known.
How it is realised on Google Cloud
Cosmos DB with a single write region, read replicas in all twelve read regions, and the change feed as the only route from authoritative state into derived state. Resolvers run on Container Apps with a workload identity holding read-only data-plane permissions; there is no connection string and no write role to steal. Front Door caches the redirect for 30 seconds and forwards misses over Private Link to the nearest regional origin group.
Options weighed
  • ChosenRead-only redirect plane fed by one-way propagation: Makes redirect availability independent of the writer, caps the write store's provisioning at the real write rate, and makes the whole read tier disposable. Costs eventual consistency, which is why ADR-15 publishes the bounds.
  • RejectedOne service reading and writing the link table on the click path: Correct in every steady-state diagram and wrong at the first hot key or bulk import. Also makes the click path a write path, which is a security property nobody intended to give away.
  • RejectedRead from the authoritative store, write nothing: Removes the write but keeps the coupling: a write-region incident or an RU exhaustion from a campaign import becomes a global redirect incident.
  • Right elsewhereStrongly consistent reads in the redirect path: Right for a system where a stale read is a correctness violation — a payment authorisation, a feature entitlement. Here a stale read means a link points at last week's page for up to five minutes, which is a published property rather than a bug.
Consequences
What it buys
  • A total management-plane outage costs zero redirects; creation fails visibly and clicks continue.
  • The entire read tier is disposable and rebuildable in 30 minutes, so a regional incident is a capacity event rather than a data event.
  • Provisioning the write store for the write rate rather than the read rate is the single largest cost saving in the design.
What it costs
  • Two propagation channels to build, operate and reason about, with two published staleness bounds.
  • A code exists authoritatively before it is resolvable everywhere, which is why a creation response carries a resolvable-within figure.
  • Debugging is harder: the question 'why did this link resolve to that?' has to be asked of a projection first and the record second.
Choose differently when
Collapse the planes when read and write rates are comparable, the whole system fits in one region, and nobody has printed anything — an internal `go/` link service for a single company is in that category and should be built as one service with one table.
Why it holds up over time
This is the decision least tied to Azure and most likely to be eroded by a feature request. Nothing here depends on Front Door, Cosmos DB or Container Apps. What would break it is real-time click counts on the redirect response, or a 'resolve the latest destination, guaranteed' option sold to one large customer — which is why the boundary is drawn in views 08 and 14 rather than left as a convention.
LessonWhen reads outnumber writes by three orders of magnitude, the read path should not be able to write, even if it would be convenient. Take the credential away and the temptation goes with it.
Shown on views07 08 14
ADR-02

Revocation travels on its own channel, ahead of the projection

Accepted

Does 'stop serving this code' use the same propagation path as 'this code now points somewhere else'?

Context
Both are changes to a link, so one mechanism is the obvious answer. But they have different deadlines by a factor of five and different payloads by orders of magnitude. An edit carries a full record and can take five minutes; a kill carries an identifier and cannot. Sizing a single channel for the tighter deadline means paying that cost on every routine edit, and at 1.2 million creations a day that is not a rounding error. Worse, the urgent case is the one with a legal and reputational clock attached: a phishing link that keeps resolving for five minutes after an analyst pressed block is the platform's worst day, and 'we propagate all changes the same way' is not a defence anyone accepts.
Decision
Revocation is a separate, aggressively propagated channel carrying a compact set of codes that must stop resolving — killed, expired early, or blocked for abuse. Regional deny-sets pull deltas every 5 seconds and are matched before any cache layer, so a region with the delta stops serving regardless of what its hot set holds. Routine changes ride the change feed with a five-minute bound. Both bounds are published. A change classifier decides which channel a write takes, and misclassification is treated as a safety defect, not a latency one.
How it is realised on Google Cloud
Event Grid topic per region for the revocation delta, consumed into an in-process set in each resolver with a 5-second refresh and an age metric. The projection path is the Cosmos DB change feed into a per-region projection worker that upserts the Redis hot set. The 30-second Front Door cache TTL is what bounds the end-to-end worst case at 60 seconds, and a synthetic probe kills a real link every minute and measures when the last PoP stops serving it.
Options weighed
  • ChosenA separate fast channel for stop instructions: Buys urgency where it is needed and nowhere else. The payload is small enough to push everywhere in seconds, and the deny-set check is cheap enough to run before the lookup.
  • RejectedOne channel sized for the 60-second deadline: Simpler to build and operate, and pays the urgent cost on all 1.2 million daily creations. It also couples the safety promise to the throughput of the routine path — a change-feed backlog becomes a safety incident.
  • DeferredCache purge per code at the edge on every revocation: A real option for tightening the bound below 30 seconds, and a slower, less predictable fan-out than an expiry. Worth adding when a customer or a regulator needs a bound the TTL cannot give.
  • RejectedDelete the link record and let the negative cache take over: Destroys the evidence, the appeal path and the click history, and makes an accidental block unrecoverable. A blocked link must remain a link.
Consequences
What it buys
  • The safety promise is met by a mechanism sized for it, and can be stated as a number: 30 s p95, 60 s p99.
  • Routine edits stay cheap, so the platform is not paying urgency tax on every campaign import.
  • The deny-set is small enough to hold in process, which makes the check free relative to the lookup it precedes.
What it costs
  • Two sources of truth on the read path, which must never disagree in the wrong direction. The deny-set therefore always wins and is checked first.
  • A classifier that can mislabel a kill as an edit, which is a silent failure — hence the synthetic probe rather than a component health check.
  • One more thing to rebuild after a regional restart, and the one whose absence is dangerous rather than merely slow.
Choose differently when
One channel is right when the urgent bound and the routine bound are the same, or when the write rate is low enough that paying urgency tax on everything is free. A platform doing a thousand creations a day should not build two channels.
Why it holds up over time
The two-bound structure should outlive the transport. Event Grid, the change feed and even the 5-second pull are implementation; what must survive is that stop instructions do not queue behind content updates, and that both bounds are numbers the platform says out loud.
LessonWhen two operations on the same object have deadlines an order of magnitude apart, they are two mechanisms wearing one name. Separate them before someone sizes the cheap one for the expensive promise.
Shown on views13 16 20
ADR-15

Staleness bounds are published as contracts, not implied as immediacy

Accepted

Does the platform tell customers how long a change takes to take effect, or let them assume it is instant?

Context
Every eventually consistent path in this design has a real bound: 60 seconds for a revocation, five minutes for a destination edit, ten seconds for a new link to be resolvable everywhere, one second in the creating region. A platform that says nothing is assumed to be instant, and then every support conversation is a negotiation, every abuse response is unaccountable, and every customer's expectation is set by whichever anecdote they heard first. Publishing a number invites arguments about the number, which is exactly the point: a number can be measured, challenged and improved, and a vague promise cannot.
Decision
Every eventual path states its bound in the product documentation, in the API response where relevant, and in the SLO set. Revocation 30 s p95 / 60 s p99; destination edit 5 minutes p99; new link resolvable globally 10 s p99, in-region 1 s p99; scheduled expiry effective within 60 s of the stated instant. Each bound has a synthetic probe measuring it end to end from outside, and a change to any bound is a product change with a release note, not a tuning adjustment.
How it is realised on Google Cloud
Probes run continuously and land in Azure Data Explorer: a synthetic link is killed every 60 seconds and its last-serving PoP measured, an edit is made and its global visibility timed, a new link's resolvability is sampled across regions. Alarms fire on the published promise rather than on component health, because every failure this platform fears looks healthy at the component level.
Options weighed
  • ChosenPublish every bound, probe every bound: Makes the design's central trade-off legible and auditable, and turns support answers into one sentence.
  • RejectedState nothing and aim to be fast: Customers assume instant, the platform is judged against that, and no internal argument about caching can be settled.
  • RejectedPublish a single conservative number for everything: Simplifies the documentation and discards the distinction that the whole two-channel design exists to create.
  • Right elsewhereOffer strong consistency as a premium tier: Coherent for a configuration or entitlement service where staleness is a correctness bug. Here it would require the read path to coordinate globally, undoing ADR-01 for the customers who pay most.
Consequences
What it buys
  • Support, sales and abuse response all use the same sentence, and it is checkable.
  • Alarms test what is sold rather than what is running, which catches the silent failures.
  • The 30-second cache TTL becomes a visible, arguable trade instead of a hidden setting.
What it costs
  • Published numbers can be missed publicly, and a missed bound is now a breach rather than an anecdote.
  • Probe infrastructure to build and keep honest, including synthetic links that must never be mistaken for customer data.
  • Tightening a bound later is a product commitment, so the initial numbers need to be defensible rather than optimistic.
Choose differently when
Skip publication only where no external party can observe the staleness — an internal cache with a single consumer that is also the writer.
Why it holds up over time
The numbers will change and the practice should not. Publishing bounds is what keeps an eventually consistent system honest over a decade of staff turnover, because the next team inherits a contract rather than a folk belief.
LessonAn unstated bound is assumed to be zero. If your system is eventually consistent, the eventual part is a number, and it belongs in the documentation.
Shown on views13 16 20

Code and identityHow a code is chosen, who guarantees it is unique, and why it can never be used twice.

ADR-03

Codes are random, unguessable and seven characters — not counter-derived

Accepted

Where does a generated code come from: a random draw, an encoded counter, a pre-allocated block, or a hash of the destination?

Context
A counter encoded to base62 is the efficient answer: dense, collision-free, no read before write. It is also enumerable, and enumerability is a disclosure vulnerability rather than an aesthetic complaint — walking the space reveals every tenant's destinations, including the unlisted document, the internal deck and the pre-announcement landing page. A globally monotonic counter is also a coordination point on the write path, which is exactly the thing a multi-region creation story cannot afford. Hashing the destination deduplicates identical links for free, which some customers want and others consider a privacy breach: two tenants shortening the same URL must not be able to detect each other. A random draw needs a uniqueness check and a retry loop that gets worse as the table fills.
Decision
Generated codes are drawn at random from a base62 alphabet with a default length of 7 characters, giving roughly 3.5 × 10¹² codes against an assumed 12 billion stored and 1.2 million created per day. Allocation is a conditional create against the reservation index, retried on conflict. Sequential or otherwise enumerable codes are never issued on shared domains. Lengthening the default is the planned response to density, and the code length is therefore a per-domain configuration rather than a constant in the code.
How it is realised on Google Cloud
A cryptographically seeded random draw in the management plane, then a conditional create (`If-None-Match: *`) against the reservation container in Cosmos DB, keyed by `(domain, code)`. At the assumed density the expected retry rate is negligible; the retry count is a metric precisely because it is the leading indicator that 7 characters have stopped being enough.
Options weighed
  • ChosenRandom base62, 7 characters, conditional create: Unguessable, no write-path coordination, and the retry cost is a function of density that can be monitored and answered by lengthening.
  • RejectedEncoded global counter: Dense and cheap, enumerable, and a coordination point. Enumerability alone disqualifies it on a shared domain.
  • DeferredPre-allocated per-region blocks: Removes coordination entirely and is the right answer if creation must survive a regional partition. Deferred because ADR-05 already accepts refusing creation during failover, and blocks add per-region accounting for a case that has not been asked for.
  • RejectedHash of the destination: Free deduplication and a cross-tenant information leak: two tenants shortening the same URL would collide into one code and learn something about each other.
Consequences
What it buys
  • A code discloses nothing about when it was created, who created it, or what exists next to it.
  • Creation needs no global sequence, so the write path has one fewer coordination point than the counter design.
  • Density is a monitored, answerable condition rather than a cliff.
What it costs
  • A read-modify-write on allocation, which is why the uniqueness authority has to be strongly consistent (ADR-05).
  • Seven characters are longer than a counter would need for the same population — the price of unguessability.
  • Retry rates climb with density, so the code length is a parameter somebody must eventually change.
Choose differently when
Use a counter when the codes are not secret and never shared outside a trust boundary — an internal short-link service, or a system where every destination is already public by construction. Use a content hash when deduplication is an explicit product feature and single-tenant.
Why it holds up over time
The alphabet and the length will change; the property must not. 'A code reveals nothing about its neighbours' is what makes shortening safe for unlisted destinations, and it is the kind of property that is quietly lost when somebody adds sequential vanity codes for a launch.
LessonAn identifier that can be enumerated is an index of everything it identifies. Decide whether that index is something you are willing to publish before choosing how to generate the identifier.
Shown on views08 15 22
ADR-04

A code is identity, and is retired permanently rather than reused

Accepted

When a link is deleted or expires, may its code be issued again to someone else?

Context
Reuse is tempting for exactly one reason: short, memorable aliases are scarce, and a customer who abandons `/spring` is holding an asset somebody else wants. But a short code's lifetime is not the platform's to decide. It is on a poster, in a printed programme, in an email thread from two years ago, in a QR code etched into a product. Reissuing it means that artefact now resolves to a stranger's destination — the worst outcome this platform can produce, worse than a dead link, because the clicker has no way to know it happened. The address space makes the trade absurd: at 7 characters there are roughly 3.5 × 10¹² codes and an assumed 12 billion in use, so reclamation recovers nothing that matters.
Decision
A code, once issued, is never reassigned to a different link and never reissued after deletion or expiry. The reservation record outlives the link record and carries a retired flag, so a claim against a retired code fails exactly as a claim against a live one does. A tenant deleting a link releases their quota, not the code. Destinations are mutable; codes are not.
How it is realised on Google Cloud
The reservation container is never deleted from. Link deletion marks the reservation retired and removes the link record; a subsequent conditional create fails against the retained reservation. Retired codes resolve to 404 through the negative cache rather than being absent from every store, so an enumerating client cannot distinguish 'never existed' from 'retired'.
Options weighed
  • ChosenPermanent retirement, reservation outlives the link: Makes the catastrophic outcome structurally impossible rather than unlikely, at a storage cost of a few tens of bytes per retired code.
  • RejectedReuse after a quarantine period: Any finite quarantine is shorter than a printed poster's life. It converts a structural guarantee into a bet on how long paper lasts.
  • RejectedReuse only for codes that never received a click: Superficially safe and wrong: a code printed for a campaign that has not launched yet has no clicks and is the most dangerous possible candidate for reuse.
  • RejectedLet the tenant decide: The risk is borne by the clicker, who is not party to the decision. This is not a tenant-configurable property.
Consequences
What it buys
  • A printed code can never resolve to somebody else's destination. The platform's worst failure mode is removed by construction.
  • The uniqueness check has one rule, not one rule plus an age exception, which is what keeps the race test in view 19 simple enough to trust.
  • Deletion becomes cheap and reversible in the only way that matters: the identity is still held.
What it costs
  • The reservation store grows monotonically and is never compacted — an accepted, small, unbounded cost.
  • Desirable aliases are permanently consumed, including by tenants who have left, and the platform must be willing to say no.
  • Support will be asked for exceptions, and the answer has to be no every time, which only holds if the mechanism makes it impossible rather than discouraged.
Choose differently when
Reuse is defensible when every link is short-lived by contract and never leaves a digital channel the platform controls — a one-time password link, a session share expiring in an hour. The moment a code can be printed, retirement is the only safe answer.
Why it holds up over time
This is the promise that ages best and is most likely to be attacked by a future team looking at a table of valuable retired aliases with a revenue number next to it. It costs almost nothing to keep and cannot be recovered once broken, because the evidence arrives years later as somebody else's QR code.
LessonIdentifiers that escape into the physical world are permanent whether or not the system treats them that way. Decide which of your identifiers can be printed, and retire those forever.
Shown on views12 15 21
ADR-05

Alias uniqueness is the only strongly consistent operation

Accepted

How is `(domain, code)` guaranteed unique when creation happens concurrently, and what consistency does the rest of the system get?

Context
Two clients asking for `go.acme.com/spring` in the same millisecond must produce exactly one winner and one clear conflict. A read-then-write cannot guarantee it. An eventually consistent store with last-write-wins conflict resolution actively hides the problem: both writers succeed, both are told they own the alias, and one of them silently loses their link later — which is worse than a rejection, because they have already sent the artwork to print. Meanwhile the read path needs the opposite property: a globally strongly consistent read on every redirect would turn a 40 ms budget into a cross-region coordination problem.
Decision
The reservation index is the single strongly consistent component in the architecture, written by conditional create in one write region. Exactly one writer wins; the loser receives a 409 naming the collision and is never given a substituted code. Everything else — the projection, the hot set, the edge cache, expiry by TTL — is eventually consistent, with its staleness published (ADR-15). Multi-region writes are refused: during a write-region failover, creation is unavailable for up to five minutes rather than risking two winners.
How it is realised on Google Cloud
A Cosmos DB container keyed by `(domain, code)` in a single-write-region account, written with `If-None-Match: *` so the insert is a genuine compare-and-set. The link record is written after the claim succeeds. Failover to the standby write region is explicit, and the management API returns a retryable error for creation throughout.
Options weighed
  • ChosenConditional create against a single-writer index: A real compare-and-set with no coordination protocol to build. Costs creation availability during failover, which the requirement accepts in exchange for zero duplicate claims.
  • RejectedMulti-region writes with conflict resolution: Keeps creation available everywhere and makes uniqueness probabilistic: last-write-wins picks a winner after both clients were told they had succeeded.
  • RejectedRead-then-write with a uniqueness index: Works until two requests interleave, which at 1.2 million creations a day is daily rather than theoretical.
  • Right elsewhereA dedicated consensus service for allocation: Right when allocation must survive a regional partition and cannot wait — a payment identifier mint, for instance. Here it is a whole distributed system added to avoid a five-minute creation outage.
Consequences
What it buys
  • Duplicate ownership of a code is impossible, which is the platform's one zero-tolerance guarantee.
  • The conflict is honest and immediate, so a customer discovers it before the artwork goes to print rather than after.
  • Exactly one component needs strong consistency, so the other twenty-two views can be designed around eventual consistency without hedging.
What it costs
  • Creation is unavailable during a write-region failover, for an assumed RTO of up to five minutes.
  • Creation latency includes a cross-region round trip for clients far from the write region, which is why the 250 ms p99 is a creation figure and not a redirect figure.
  • Bulk imports must be resumable, because a failover mid-import is a normal event rather than an exception.
Choose differently when
Multi-region writes become correct as soon as uniqueness stops being global — per-region namespaces, or codes that embed their origin region. If the product can accept `eu/spring` and `us/spring` as different links, this decision dissolves.
Why it holds up over time
The technology will change; the rule should not. 'Exactly one thing is strongly consistent, and it is named' is what keeps a distributed system arguable. Systems decay when the list of strongly consistent things grows quietly, one convenience at a time.
LessonFind the single operation that cannot tolerate a conflict and give it the strongest guarantee you have. Then make everything else eventually consistent on purpose, and say so.
Shown on views11 12 15
ADR-06

The code namespace is per domain, not global

Accepted

Is `spring` one alias across the whole platform, or one per domain?

Context
A global namespace is simpler: one key, one index, one uniqueness rule. It also means the first customer to claim `sale` takes it from every other customer forever, which is indefensible when 12,000 of those customers are paying for their own branded domain. Per-domain namespacing is what a branded-domain customer already assumes — they bought `go.acme.com` precisely so that its paths are theirs — and it multiplies the reservation index's key space by the number of domains, which is a scale question rather than a correctness one. The shared domain is the interesting case, because there the namespace really is contested and brand-adjacent aliases are a trust problem rather than a first-come-first-served one.
Decision
The namespace key is `(domain, code)`. Two tenants may each own `spring` on their own domains. On the shared domain a reserved-name deny-list applies — operational paths, single characters, profanity, and strings resembling well-known brands — and generated codes are always unguessable (ADR-03). Branded domains are first-class: same latency objective, same availability objective, same code space semantics.
How it is realised on Google Cloud
Reservation and link containers are both partitioned on domain with code as the row key, so a branded domain's claims and lookups are local to its partition, and a new domain adds partitions rather than contending for an existing one. Front Door serves all domains from the same anycast address set with per-domain managed certificates.
Options weighed
  • ChosenPer-domain namespace: Matches what a branded-domain customer already believes, and turns namespace scarcity into a shared-domain question only.
  • RejectedGlobal namespace: Simpler index, unsellable product: a customer on their own domain cannot be told their alias is taken by a stranger.
  • RejectedPer-tenant namespace with domains as a display concern: Breaks as soon as one tenant has two domains and wants the same alias on both — routine for a company running regional sites.
Consequences
What it buys
  • Branded domains are genuinely first-class rather than a premium veneer over a shared namespace.
  • Partitioning by domain keeps each customer's claims and reads local, which also bounds the blast radius of a hot domain.
  • Namespace policy can differ between the shared domain and customer domains, which is where the real difference in risk is.
What it costs
  • The reservation index is larger and its key is compound, which every component must handle consistently.
  • Cache keys, metrics and quotas all carry the domain, so a bug that drops it becomes a cross-tenant defect rather than a miss.
  • Domain deletion has to be handled explicitly: its codes retire with it and cannot be freed.
Choose differently when
A global namespace is right for a single-domain product — a social network's own link wrapper, where there is exactly one host and the operator owns every alias.
Why it holds up over time
Compound keys are hard to add later and easy to keep. A platform that starts global and grows branded domains has to migrate its uniqueness authority under live traffic, which is the kind of migration that produces exactly the duplicate-ownership defect ADR-05 exists to prevent.
LessonNamespace boundaries follow ownership boundaries. If a customer can own the host, they own its paths, and the key has to say so from the first row written.
Shown on views05 10 12

The read pathWhere a lookup happens, what it is allowed to depend on, and what it returns.

ADR-07

Resolution happens in twelve regional resolvers, not at the PoP

Accepted

Does the lookup run at the edge against a replicated dataset, or in a regional service the edge forwards to?

Context
The fastest design puts the whole link table at every PoP: no origin hop, uniform tail latency, and revocation as a data push. Twelve billion records at roughly 1 KB is around 12 TB, which is not a PoP-resident dataset, so in practice this means a hot subset at the edge — and that needs a programmable edge with a replicated key-value store. Azure does not offer one for general-purpose use: Front Door runs rules and caching, not arbitrary code against a replicated store. The honest options are therefore an edge response cache in front of regional resolvers, or origin-only resolution with CDN caching of the redirects themselves, which is cheapest and hardest to revoke.
Decision
Resolution runs in twelve stateless read regions. Front Door terminates TLS at the nearest PoP, applies WAF and bot rules, serves the 30-second response cache, and forwards a miss over Private Link to the nearest regional origin group. Each resolver walks an in-process LRU, a regional Redis hot set and an in-region Cosmos replica. Region count and placement are sized to hold the 40 ms p99 for the assumed traffic distribution, and are expected to change with real geography.
How it is realised on Google Cloud
Front Door Premium with latency-based origin selection across twelve Container Apps environments, each with a zone-redundant Azure Cache for Redis and an in-region Cosmos DB read replica. The 97% assumed edge hit rate means the regional tier is sized for the miss traffic, not the full rate — which is what makes twelve regions affordable.
Options weighed
  • ChosenEdge response cache plus regional resolvers: Uses what the platform actually offers, keeps the lookup in one place that can be reasoned about, and puts the cache where the traffic is. Costs a PoP-to-region hop on every miss.
  • RejectedFull link table replicated to every edge location: Fastest and uniform, and a 12 TB distribution and consistency problem of its own. Not available on this platform in any case.
  • DeferredHot subset in a programmable edge KV store: The right answer the moment the platform offers a replicated edge store at a sane price, and the first place the architecture should move. Nothing above the read path needs to change when it does.
  • RejectedOrigin-only with long-lived CDN caching of redirects: Cheapest per redirect and incompatible with a 60-second revocation bound: the cache becomes the authority and cannot be corrected quickly.
Consequences
What it buys
  • One lookup implementation, in one kind of place, with one set of metrics — rather than logic split between edge rules and a service.
  • The regional tier is sized for misses, so the 97% hit rate converts directly into fleet size and cost.
  • A read region is disposable: no unique state, so losing one is a capacity event.
What it costs
  • A cold code pays the PoP-to-region hop, which is the click a customer tests with and complains about — hence pre-warming at creation.
  • Twelve regions is twelve caches to warm and twelve deny-sets to keep fresh.
  • The p99 depends on region placement assumptions that only real traffic can validate.
Choose differently when
Move the lookup to the edge as soon as a replicated edge key-value store is available and affordable, or if measured traffic turns out to be concentrated enough that the hot subset is small. Move it the other way — fewer, larger regions — if the miss rate is far below the assumption and placement is buying nothing.
Why it holds up over time
This is the decision in the package most tied to 2026 rather than to architecture, and it is designed to be replaced. What must survive the replacement is the boundary: whatever serves the lookup reads a projection and holds no write credential (ADR-01).
LessonName the decisions that are statements about this year's platform rather than about the problem. They are the ones to make replaceable, and the ones to revisit first.
Shown on views09 14 18
ADR-08

A 302 with a 30-second cache lifetime, never a permanent redirect

Accepted

What status code does a redirect carry, and how long may a browser or intermediary remember it?

Context
A permanent redirect is the fastest thing the platform could possibly return: the browser never asks again, the platform never pays for the second click, and the destination inherits the link's search ranking — which is why SEO advice recommends it. It also makes the link uneditable and unkillable for every client that cached it, for as long as that client chooses to remember, which can be indefinitely. Since editing and killing are the product, and since clicks are a billing input, the fast answer is the wrong one. The real design space is the cache lifetime, and it is a single dial with safety on one side and cost on the other.
Decision
The redirect is a 302 with an explicit cache lifetime of 30 seconds at shared caches and no-store semantics for the terminal states. The 30 seconds is a design parameter, not a tuning knob: it is simultaneously the hot-key absorber, the dominant lever on cost per million redirects, and the outer bound on the revocation promise in ADR-02. Any proposal to change it must restate the revocation bound in the same sentence.
How it is realised on Google Cloud
Front Door caches the 302 for 30 seconds keyed on host plus code; the response carries `Cache-Control: max-age=30`. Blocked, expired and unknown responses are marked no-store so a terminal state is never held by a client. Clicks are still counted, because they are taken from the edge access log rather than from application code (ADR-10) — which is the only reason caching and analytics can coexist here.
Options weighed
  • Chosen302 with a 30-second shared-cache lifetime: Keeps every link editable and killable within a stated bound, absorbs hot keys at the edge, and keeps counting exact because the count comes from the log.
  • Rejected301 permanent redirect: Fastest and cheapest, and it permanently forfeits both revocation and per-click counting for cached clients. A link platform that cannot kill a link is not a link platform.
  • Rejected302 with no caching at all: Tightest possible revocation and the loss of the mechanism that absorbs a viral code. Every click becomes an origin request at 150,000 per second.
  • Right elsewhere301 for links the customer marks permanent: Defensible for a pure site-migration tool, where SEO transfer is the product and nobody will ever repoint the link. It does not belong in a platform whose value is that the destination can change.
Consequences
What it buys
  • Every link stays editable and killable within a published bound, for every client.
  • The edge cache absorbs hot keys and carries the assumed 97% of traffic, which is what makes the cost target reachable.
  • Terminal states are never cached, so a blocked or expired link cannot be resurrected by a stale client.
What it costs
  • Every 30 seconds, a popular code costs an origin resolution — the price of remaining changeable.
  • No SEO ranking transfer, which has to be explained to customers who have read otherwise and will ask for a 301.
  • One number now carries three concerns at once, so it can only be changed by someone who understands all three.
Choose differently when
A permanent redirect is correct when the mapping genuinely cannot change and nobody needs per-click data — a retired domain's canonical move, or a published DOI-style identifier with a permanence policy.
Why it holds up over time
Status codes and cache directives are the most stable technology in the design; HTTP semantics have outlived every platform they ran on. The pressure on this decision will come from cost reviews looking at the cheapest line on the bill, which is why the 30 seconds is documented as three coupled promises rather than as a cache setting.
LessonA cache lifetime on a redirect is not a performance setting. It is the upper bound on how quickly you can correct anything you have ever told a client.
Shown on views14 16 20
ADR-09

The link table is partitioned by code hash, not by tenant

Accepted

What is the partition key of the link table: the tenant, or the code itself?

Context
Partitioning by tenant is the instinct of anyone who has built a multi-tenant system, and it makes tenant-scoped operations — list my links, export my campaign, delete my account — cheap. It also guarantees a hot partition, because tenants are not uniform: an assumed p99 tenant holds two million links, and the largest holds far more, while the redirect path reads by code and never by tenant. The read path's requirement and the management path's requirement point in opposite directions, and the read path outnumbers the other by 500:1.
Decision
The link table is keyed and partitioned on `(domain, code)`, so a point read by code hits exactly one partition and the distribution follows the hash rather than the customer. Tenant-scoped listing and search are served by a separate index built from the same change feed. Redirect-path cost and latency are never a function of how many links a tenant owns.
How it is realised on Google Cloud
Cosmos DB containers partitioned on domain with code as the row key; the by-tenant index is a separate container maintained by a change-feed consumer. A tenant with two million links therefore spreads across many physical partitions and cannot become a hot one.
Options weighed
  • ChosenPartition by code within domain: Makes the hot path a single-partition point read and the distribution independent of tenant size. Costs a second index for listing.
  • RejectedPartition by tenant: Cheap listing, guaranteed hot partitions, and a redirect latency that varies with which customer owns the code.
  • RejectedPartition by tenant with a global secondary index by code: Puts an index lookup on the 150,000-per-second path to avoid one on the 14-per-second path.
Consequences
What it buys
  • Redirect cost and latency are independent of tenant size, so onboarding a very large customer is not a read-path event.
  • Hot partitions can only come from genuinely hot codes, which the caches are designed to absorb.
  • The write path spreads naturally across partitions during a bulk import.
What it costs
  • A second index to build, maintain and reconcile, with its own lag.
  • Tenant-scoped deletion is a fan-out rather than a partition drop.
  • Per-tenant RU accounting is derived rather than physical, which makes cost attribution a calculation.
Choose differently when
Partition by tenant when the dominant access pattern is tenant-scoped — an analytics warehouse, a per-customer export service — or when tenants are small and uniform enough that no partition can get hot.
Why it holds up over time
Partition keys are the hardest thing in a data platform to change later, because changing one is a full migration under live traffic. Choosing it for the dominant read pattern rather than the convenient administrative one is a decision that pays for a decade.
LessonPartition for the access pattern that happens most, not the one that is easiest to write a query for. Build an index for the other one.
Shown on views11 12 18

CountingHow a click becomes a number somebody bills on, without a write on the redirect path.

ADR-10

Clicks are collected from the edge access log, after the response has left

Accepted

What records a click: the resolver that served it, or the edge that logged it?

Context
Once the redirect is cached for 30 seconds (ADR-08), the resolver simply does not see most clicks — an assumed 97% of them never reach application code. Counting in the resolver would therefore make the click count a function of the cache hit rate, which is both wrong and invisible: the dashboard would look healthy and the numbers customers are billed on would silently drop as caching improved. The alternative is to treat the edge's own request log as the source of the count, which also removes the last reason for the redirect path to write anything.
Decision
One click event per redirect served is derived from the edge access log, emitted after the response has left the PoP, durably buffered before processing. The resolver's own log is a second, smaller stream used for cache-miss analysis and never for counting — one authority per number. A failure anywhere in the click path can never surface as a failed or slowed redirect.
How it is realised on Google Cloud
Front Door diagnostic logs stream to Event Hubs with seven-day retention as the durable buffer, giving an assumed 30-second RPO on click data. Processing classifies traffic, enriches, drops identifiers and appends to Azure Data Explorer. Daily reconciliation compares landed events against the edge's own request counters within ±0.5%.
Options weighed
  • ChosenCount from the edge access log: The only option compatible with caching the redirect. It also makes the redirect path genuinely write-free, which is the security property in ADR-01.
  • RejectedCount in the resolver: Makes the count a function of the cache hit rate, and the error is invisible and moves in the direction that looks like success.
  • RejectedCount in the resolver and disable caching: Exact counting at 150,000 origin requests per second, and the loss of the mechanism that absorbs a viral code.
  • RejectedCount client-side with a beacon: A redirect has no page to run script on, and a platform that inserts an interstitial to count clicks has changed the product to suit its telemetry.
Consequences
What it buys
  • Caching and accurate counting stop being in conflict, which is what makes the 30-second TTL affordable.
  • The redirect path holds no write credential at all (ADR-01), because nothing on it needs one.
  • An analytics outage is invisible to clickers and bounded by the buffer's retention rather than by anyone's reaction time.
What it costs
  • The count depends on the edge log being complete and timely; a sampled or truncated log is a billing dispute rather than a monitoring gap.
  • Click detail is limited to what the edge sees, so anything richer requires an enrichment step rather than an application field.
  • Two log streams to keep straight, with a standing rule about which one counts.
Choose differently when
Count in the application when responses are not cacheable and the application is on the path for every request anyway — a signed download URL issuer, an authenticated API. There, the application log is the edge log.
Why it holds up over time
This will still be the right answer on any edge platform that produces a request log, and it is the reason two other decisions hold. The fragile version — counting in application code — looks identical on a dashboard, which is precisely why it needs to be written down as a decision rather than left as an implementation detail.
LessonPut the meter where every request passes, not where your code happens to run. If caching can hide traffic from your counter, your counter is measuring your cache.
Shown on views14 17 20
ADR-11

Full click events are shipped, with sampling reserved for pathological codes

Accepted

Does the platform ship one event per click, or aggregate counters at the edge and ship rollups?

Context
Edge aggregation is dramatically cheaper: a counter per code per minute instead of 150,000 rows per second. It also forecloses every question nobody thought to ask at design time — the fraud investigation that needs individual events, the customer dispute that needs a timeline, the bot reclassification that needs to be replayed over history. Sampling above a per-code threshold bounds cost on viral links, which is exactly where the customer is watching most closely and least willing to accept an estimate. And click data is a billing input here, which raises the bar on what can be reconstructed later.
Decision
One event per click is shipped and retained at full fidelity for 90 days, rolled up into daily aggregates for 25 months and monthly for 5 years. Totals and breakdowns are exact; unique-visitor figures are estimates with a stated ≤ 2% relative error and are labelled as estimates everywhere they appear. Adaptive sampling above a per-code threshold is deferred to Phase 3 and, if enabled, must restate the accuracy contract rather than quietly degrading it.
How it is realised on Google Cloud
Event Hubs to Azure Data Explorer, with rollups as materialised views rather than a separate batch pipeline. Traffic class is stored on the raw event, so a reclassification is a replay over retained data rather than a request to re-collect it. Raw retention is capped at 90 days because it is the dominant analytics cost line.
Options weighed
  • ChosenFull events for 90 days, then rollups: Keeps later questions answerable — fraud, disputes, reclassification — and caps the cost with retention rather than with fidelity.
  • RejectedEdge-aggregated counters only: A fraction of the cost and no way to answer any question that was not anticipated, including the ones that arrive as legal requests.
  • DeferredSampling above a per-code threshold from day one: The right lever if viral codes turn out to dominate cost, and the wrong default: it degrades accuracy exactly where customers are looking hardest.
  • RejectedFull events retained indefinitely: Storage grows without bound for questions that are almost never asked beyond a quarter, and increases the privacy surface for no product gain.
Consequences
What it buys
  • Fraud, dispute and reclassification questions remain answerable for a quarter without re-collecting anything.
  • Exact and estimated figures are separated and labelled, so a customer is never told an estimate is a count.
  • Rollups make the common query cheap while the raw data stays available.
What it costs
  • Raw retention is the dominant analytics cost line and must be defended at every cost review.
  • A 90-day cliff means an investigation that starts late has less to work with, which has to be stated rather than discovered.
  • Full-fidelity events carry a privacy obligation, met by dropping identifiers at ingestion (ADR-12's realisation).
Choose differently when
Aggregate at the edge when click data is a reporting nicety rather than a billing input, and when nobody will ever need to reconstruct an individual request. An internal link service is exactly that case.
Why it holds up over time
The 90-day figure will move. What should not is the separation between exact and estimated numbers, and the rule that enabling sampling means publishing a new accuracy contract. Quiet accuracy degradation is how analytics platforms lose credibility permanently.
LessonDecide whether your telemetry has to answer questions you have not thought of yet. If it does, keep events and cap retention; do not keep counters and cap questions.
Shown on views12 17 20
ADR-12

Bots are classified and served, never blocked or silently counted

Accepted

What does the platform do with the assumed 18% of traffic that is unfurlers, scanners and prefetchers?

Context
These clients are not attackers. A chat unfurler fetching a link so a user sees a preview, a mail provider scanning for malware, a security tool checking a destination — all of them are doing work the platform's customers actively want done. Blocking them breaks link previews everywhere, which customers experience as the platform being broken. But counting them as human clicks is worse in a subtler way: a campaign owner who sees three clicks before announcing anything concludes the platform is inventing numbers, and every figure loses credibility at once. The third failure mode is the dangerous one: classifying a genuine click as a bot discards it invisibly.
Decision
Bot traffic is classified at the edge, served normally, tagged on the click event, excluded from customer-facing counts and from tenant quotas, and retained in the raw data with its class. Classification tags and never blocks. Because the class is stored rather than applied as a filter at collection time, a reclassification can be replayed over retained history rather than requiring the data to be collected again.
How it is realised on Google Cloud
Front Door bot manager rules produce a classification that is carried into the access log and onto the click event. Customer-facing queries filter on traffic class; cost and billing reports carry the bot share explicitly, so a tenant whose links are hammered by scanners is visible rather than mysterious.
Options weighed
  • ChosenClassify, serve, tag, exclude from counts: Keeps previews working, keeps customer numbers credible, and keeps the decision reversible because the class is data rather than a filter.
  • RejectedBlock bots at the edge: Breaks the preview behaviour customers depend on, and produces support tickets that look like platform outages.
  • RejectedCount everything as a click: Cheapest and destroys trust in every number the platform reports, starting with day-one campaign figures.
  • RejectedDrop bot events at collection: Saves storage and makes a misclassification permanent: a genuine click discarded as a bot cannot be recovered.
Consequences
What it buys
  • Link previews keep working, which is invisible when right and very visible when wrong.
  • Customer-facing numbers survive contact with a launch day dominated by unfurlers.
  • A classifier improvement can be applied retroactively, because the class is stored alongside the event.
What it costs
  • Bot traffic is served and paid for — an assumed 18% of the redirect bill for traffic nobody is billed for.
  • Two sets of numbers exist (with and without bots), and every report has to be explicit about which it shows.
  • Classification accuracy becomes a product concern with its own false-positive cost.
Choose differently when
Blocking becomes correct when the traffic is genuinely abusive rather than functional — a scraper enumerating codes is handled by rate limiting in ADR-03's threat model, not by this decision.
Why it holds up over time
The specific classifier will be replaced repeatedly. What must survive is the separation of serving from counting, and the rule that classification is recorded rather than applied destructively. Systems that filter at collection time can never correct a classification error.
LessonClassify, do not filter, when the classification might be wrong. Storing the label keeps the decision reversible; applying it at ingestion makes every mistake permanent.
Shown on views03 17 20

Trust and tenancyScreening, terminal states, and the 12,000 domains the platform serves on customers' behalf.

ADR-13

Destinations are screened before issuance and re-screened afterwards

Accepted

Is a destination checked once at creation, continuously, or not by the platform at all?

Context
A short link hides its destination — that is the product, and it is also why phishing campaigns prefer shorteners to bare URLs. A one-time check at creation catches the naive case and misses the standard technique entirely: register a clean page, get it approved, distribute the link widely, then change the page. The destination the platform approved and the destination the clicker reaches are separated by however long the campaign takes to spread. Meanwhile the platform's shared domain is a single shared reputation: once mail providers and browsers blocklist it, every legitimate tenant's links stop working, and recovery takes weeks the business does not have.
Decision
Every destination is screened against multiple reputation feeds before a code is issued, and re-screened periodically thereafter for as long as the link is live. A bad verdict at creation refuses the link; a bad verdict afterwards blocks it through the revocation channel with a platform interstitial. Bulk creation from new or unverified accounts is rate-limited and challenged. Every block and every reinstatement records the feed, the score, the reviewer and the time, retained two years, with an appeal path that runs the same code as the block.
How it is realised on Google Cloud
Azure Functions call three third-party reputation feeds synchronously at creation and on a scheduled fan-out afterwards, prioritised by live click rate so the links actually being clicked are re-screened most often. A block publishes to the revocation channel (ADR-02) so it is effective globally within 60 seconds. Evidence lands in the append-only store with an immutability policy.
Options weighed
  • ChosenScreen at creation and re-screen periodically: Addresses the actual attack — approval followed by weaponisation — and makes the platform's response time a number rather than a hope.
  • RejectedScreen at creation only: Catches the naive case and misses the standard one. It also gives a false sense of coverage, which is worse than none.
  • RejectedScreen at click time: Strongest protection and incompatible with the read path: a synchronous third-party call would dominate the 40 ms budget and make redirects depend on a vendor's uptime.
  • RejectedNo screening; respond to reports: Cheapest and ends with the shared domain blocklisted, which takes every legitimate tenant down with it.
Consequences
What it buys
  • The platform's domain reputation is defended continuously rather than after an incident.
  • Response time to a flipped verdict is bounded by the revocation promise, so it can be stated to a regulator or a customer.
  • Every decision is evidenced, which makes an appeal a process rather than an argument.
What it costs
  • Three feed subscriptions, a re-screening budget proportional to live links, and false positives that block legitimate customers.
  • Creation latency includes a synchronous feed call, which is part of the 250 ms p99.
  • A human review queue with a rota — a staffing commitment, not just a component.
Choose differently when
Skip screening only when destinations cannot be attacker-controlled — an internal shortener over an allow-listed set of hosts. As soon as a stranger can supply the URL, screening is a requirement.
Why it holds up over time
Feeds and scores will change completely; the shape will not. Screen before issuance, re-screen while live, block through the fast channel, keep the evidence. A platform that treats abuse as an operational afterthought rediscovers this list under pressure.
LessonIf your system republishes content somebody else controls, the check at admission is the easy half. The hard half is noticing when what you admitted has changed.
Shown on views06 08 16
ADR-14

Four distinct terminal states, owned by the platform

Accepted

What does a code return when it is unknown, expired, deactivated or blocked — and whose page is it?

Context
One 404 for all four is the simplest thing to build and tells the clicker nothing, which means every dead link becomes a support ticket for the customer and a mystery for the person holding the poster. A fully tenant-branded page for all four keeps the customer's experience intact and lets a blocked link masquerade as a working one, which is exactly the outcome an abusive tenant wants. There is also a real tension with enumeration: distinguishing 'never existed' from 'expired' tells a scanner which codes have been used. And a blocked link raises the question of whose reputation is on the page — the platform is making an accusation, so it should be the one making it.
Decision
Four states, four distinct responses: unknown returns 404, expired and deactivated return 410 with a tenant-configurable page, and blocked returns a platform-owned interstitial that names the reason and offers an appeal route to the owner. None is cached. The destination is never taken from the request — only from a stored record — so the platform can never be turned into an open redirect. A blocked link is never a silent 404 and never a silent redirect.
How it is realised on Google Cloud
The resolver's terminal-state selection runs last in the pipeline (view 09) so state can never collapse into a generic error. Unknown codes are negatively cached briefly, which makes enumeration cheap to serve, and retired codes look identical to never-issued ones from outside.
Options weighed
  • ChosenFour distinct states, platform interstitial for blocks: The clicker learns what happened, the owner learns why, and an accusation is made by the party making it.
  • RejectedOne generic 404 for all four: Simplest, and converts every expiry and every block into a support conversation with no information in it.
  • RejectedTenant-branded pages for all four including blocks: Lets a blocked link look alive and makes the platform's safety decision invisible to the person it protects.
  • RejectedBlock by removing the record entirely: Destroys the evidence and the appeal path, and makes an accidental block unrecoverable.
Consequences
What it buys
  • A dead link is self-diagnosable by both the clicker and the owner.
  • Blocked links carry a warning rather than a shrug, which is the difference between protecting someone and confusing them.
  • The open-redirect class of vulnerability is structurally excluded, not filtered.
What it costs
  • Four responses to build, test and keep consistent across twelve regions.
  • Distinguishing states leaks a little existence information, mitigated by unguessable codes and rate limiting rather than by ambiguity.
  • The interstitial is a platform-branded page in the middle of a customer's campaign, which some customers will dislike.
Choose differently when
A single generic response is defensible for an internal service where every clicker can ask the owner directly. It is never defensible when the clicker is a stranger who has no way to find out what happened.
Why it holds up over time
Status-code semantics outlive platforms. The pressure here will come from customers wanting full branding on every state, and the answer must remain no for blocks specifically, because the whole value of the interstitial is that it is not the accused party's page.
LessonError responses are part of the product for anyone who cannot ask you what went wrong. Distinguish the cases your users can act on differently.
Shown on views04 09 21
ADR-16

Branded domains and their certificates are the platform's responsibility

Accepted

Who owns verification, certificate issuance and renewal for 12,000 customer-owned domains?

Context
A branded domain is what an enterprise customer is actually buying: `go.acme.com` rather than a shared host nobody trusts. That makes their domain part of the platform's serving path, and its certificate part of the platform's availability. At 12,000 domains, renewal is not an administrative task — it is 12,000 independent expiry dates, each of which takes one customer's entire link estate down when it lapses. Leaving it to the customer means the platform's most valuable tier has the least reliable links, and the outage is discovered by the customer's own users. There is also a trust question at verification: serving a host the platform has not proven the customer controls is an open invitation to hijack somebody else's traffic.
Decision
The platform verifies domain ownership before serving a domain, provisions and auto-renews its certificate, and surfaces verification and renewal state to the customer before expiry becomes an outage. A branded domain carries the same latency and availability objectives as the shared domain; it is not a slower or less reliable path. Customer-supplied certificates remain available for enterprises that insist, with the renewal obligation explicitly transferred and alarmed.
How it is realised on Google Cloud
Azure DNS-based verification of a TXT or CNAME record, then Front Door managed certificates with automatic renewal, all domains served from the same anycast address set. Renewal state is a first-class field with alarms at 30, 14 and 7 days, and a certificate approaching expiry without renewal pages somebody rather than emailing the customer.
Options weighed
  • ChosenPlatform-managed verification and certificates: Removes 12,000 expiry dates from customers who will not track them, and keeps the branded path as reliable as the shared one.
  • RejectedCustomer-supplied certificates only: Cheaper and transfers an availability risk to the party least equipped to manage it, while the platform still gets the outage.
  • RejectedServe branded domains on a separate, simpler path: Makes the premium tier the slower tier, which inverts the value proposition.
  • RejectedSkip ownership verification for speed of onboarding: Lets a customer claim a host they do not control, which is traffic hijacking with the platform as the instrument.
Consequences
What it buys
  • A customer's links do not stop working because somebody forgot a renewal.
  • Branded and shared domains share one serving path, so there is one set of latency and availability numbers rather than two.
  • Ownership verification closes a hijacking vector before the first request is served.
What it costs
  • Certificate lifecycle at 12,000-domain scale is a subsystem with its own failure modes and its own on-call load.
  • Onboarding is slower because verification is a prerequisite, which sales will push back on.
  • Customer-supplied certificates remain a supported exception, which means two paths to keep working.
Choose differently when
Leave certificates to the customer when there are tens of domains and each has a named technical owner — a handful of enterprise integrations rather than a self-service tier.
Why it holds up over time
Automated issuance and renewal will get easier, and the obligation will not move. Whoever serves the host owns its availability, regardless of who owns the name, and a platform that pretends otherwise learns this from a customer's incident review.
LessonIf you serve traffic on somebody else's domain, you own that domain's availability in practice. Take the renewal job rather than the support ticket.
Shown on views05 10 18

Every package used, in one table

Eleven terms that mean something specific in this package. Where a term is used loosely elsewhere in the industry, the definition here is the one the twenty-three views were drawn against.

PackageWhat it isWhat it does hereConsidered instead
Code The short string after the host — the `spring` in `go.acme.com/spring`. Permanent identity. Issued once, never reassigned, never reissued after retirement. Treating it as a lookup key that can be recycled, which eventually resolves a printed QR code to a stranger's site.
Destination The URL a code currently resolves to. Mutable state. Changing it is the product's central feature, bounded by the five-minute propagation promise. Treating the destination as part of the link's identity, which makes every campaign change a new code and defeats the point of shortening.
Projection The read-optimised, eventually consistent copy of the link table that the redirect path actually reads. Disposable. Rebuildable from the link table in 30 minutes with no operator input. Calling the hot set a cache in front of the database, which invites somebody to read through it and then write back.
Deny-set A compact set of codes that must stop resolving now, propagated on its own channel and checked before any cache layer. The mechanism behind the 60-second revocation promise. Relying on cache invalidation, which couples the safety promise to the slowest purge in the fleet.
Reservation The claim record for `(domain, code)`, strongly consistent, which outlives the link it once identified. The uniqueness authority and the retirement ledger in one. Deriving uniqueness from the link table, which frees a code the moment its link is deleted.
Terminal state One of four distinct non-redirect outcomes: unknown (404), expired (410), deactivated (410), blocked (interstitial). What a clicker can act on. Never collapsed into a single generic error. One 404 for all four, which turns every dead link into a support ticket and every blocked link into a mystery.
Traffic class The classification of a request as human, unfurler, scanner or unknown, decided at the edge. Excluded from customer-facing counts and tenant quotas, retained in the raw data so a reclassification can be replayed. Blocking bots, which breaks link previews in every chat client and mail scanner the platform's customers rely on.
Edge hit rate The share of clicks answered by the 30-second response cache without reaching a resolver. The dominant lever on both tail latency and cost per million redirects; assumed 97%, alarmed below 94%. Treating it as a performance metric only, when it is the main line on the bill.
Propagation bound The published maximum time for a change to take effect globally — 60 s for a revocation, 5 minutes for an edit. A contract. Support answers, customer expectations and abuse response all derive from it. Implying immediacy, which makes every support conversation a negotiation and every abuse response unaccountable.
Working set The share of stored links that receive a click in a 30-day window; assumed 4%. What makes a hot set affordable against 12 billion stored links. Sizing the cache against the table, which prices the architecture out before it is built.
Click One redirect served, recorded from the edge access log after the response has left. The billing and reporting unit, reconciled daily to ±0.5% with a 0.05% loss budget. A counter incremented by application code, which makes caching and accurate counting mutually exclusive.
Open svg/<view>.svg or drawio/<view>.drawio in draw.io Desktop or at app.diagrams.net to edit. The SVG carries the diagram inside it, so it is both the picture and the source. This folder is self-contained — copy it whole and every link still resolves.