PaddySpeaks · Systems at the Whiteboard · Nº 39

The Career Problem

Model the data behind a professional network that is graded on hires, replies and confirmed mentorships instead of sessions and impressions. The engagement label arrives in milliseconds; the professional label arrives in ninety days, is never observed at all in sixty percent of cases, and half the time happens in a building you have no telemetry inside. One prompt, a fact table whose truth is not knowable for a quarter, a consent flag that must gate the write rather than the read, and a reputation score that has to be reproducible on demand or it is a rumour. A complete working-through of data flow, schema, consent-gated Python ingestion, long-horizon attribution SQL, the reproducibility check, and the dashboard that proves it.

◆ At a glance the 20-second version — full walk-through below

The senior move

Split the spine in two. fct_surfaced is written now and is immutable; fct_outcome arrives up to 120 days later and is joined through a re-runnable brg_outcome_attribution. Attribution is an opinion with a version, never a column on the fact.

Four grains

  • Surfaced — every ranked item shown, ~10 B/day, carrying its reason codes.
  • Interaction — the same-session reaction; cheap, fast, and not the objective.
  • Outcome — reply, interview, placement, confirmed mentorship. Sparse, late, often external.
  • Evidence — claims and the confirmations that make them count.

Key SQL

  • Interval join + attribution_run_id → 120-day outcome credit
  • NOT EXISTS on absence → recruiter ghosting rate
  • As-of SCD2 replay → reputation reproducibility diff
  • Anti-join as assertion → the consent-leak audit that must return zero rows

The trap

Filtering job-search signals at read time. Not looking has to gate the write — nothing is collected — or the toggle is a lie the schema can prove. And you cannot A/B a career: measure the challenger by shadow ranking, never by withholding a match.

§ 01 — THE QUESTIONThe label arrives ninety days late

Every recommendation system you have modelled before had a label you could read in seconds. A click, a like, a watch. This one does not. The thing this product exists to cause — someone gets a job — happens weeks after the row was written, in a building you have no telemetry inside, and more often than not you never find out it happened at all.

Interview Prompt

"Design the data platform for a professional network whose stated objective is professional outcomes rather than engagement — placements, replies that go somewhere, mentorships both sides confirm. A billion members, ten billion ranked items surfaced a day. Every surfaced item must be able to explain itself. A member can switch to 'not looking' and we promise we stop collecting job-search signal entirely. And the reputation score we show people has to be defensible line by line. How do you model it?"

LEVEL · SENIOR / STAFFDURATION · 45–60 MINFORMAT · WHITEBOARD

Most candidates hear "professional network" and reach for the feed model they already know: impressions, engagements, a ranker, a rollup. That model is not wrong so much as it is answering a different question, and the interviewer is waiting to see how long it takes you to notice. A feed's objective function is available almost immediately — the like lands two seconds after the impression, in enormous volume, on the same platform, attributable to the row that caused it. Everything about the feed architecture follows from that gift. Take the gift away and the architecture inverts. Here the objective is a hire, and a hire is sparse (roughly one confirmed placement per four hundred million surfaced items), late (median forty-seven days from the surfaced row that started it, ninety-five percent inside a hundred and twenty-eight), and largely unobservable (about sixty percent of hires are never reported on the platform at all). You are being asked to build a measurement system for an event you mostly cannot see.

That single fact reorganizes everything. It means the fact table cannot carry its own outcome, because the outcome does not exist when the row is written and may never exist. It means attribution across a four-month window is a rebuildable opinion rather than a fact, so it needs a version and a re-run, exactly like ad attribution but with a hundred times the latency. It means engagement metrics are still collected but are explicitly not the objective, and the schema should make that demotion structural rather than cultural. And it means the counterfactual question — what would have happened if we had surfaced something else — cannot be answered the way a feed answers it, because you may not ethically withhold a good job match from a person to measure lift. So before any boxes and arrows, the working frame:

THE SURFACED GRAIN
Surfaced items. One row per (persona × item × surface × slot) every time the product ranks something and shows it — a role, a person, an event, a piece of writing. Ten billion a day. Each row freezes the ranking_model_id, the predicted_outcome_score, and — the requirement that makes this product different — the reason_codes that would be shown if the member clicked "Why this?". The explanation is data, written at serve time, not prose generated later.
THE INTERACTION GRAIN
Interactions. Opened, saved, dismissed, messaged, applied. Append-only, same-session, cheap, plentiful — and deliberately modelled as a leading indicator rather than the objective. This is the grain a feed would have called success. Here it is instrumentation.
THE OUTCOME GRAIN
Outcomes. A reply that got a real answer, a screen, an interview, an offer, a placement, a mentorship both parties confirmed, an introduction that led somewhere. One row per outcome, carrying observed_via — platform-observed, self-reported, or employer-verified — because the provenance of a label is part of the label. Sparse, late, and joined to the surfaced spine only through a versioned attribution bridge.
THE EVIDENCE GRAIN
Claims and confirmations. "Mentored six engineers" is a claim; four confirmations from named mentees are what let it count. The reputation score is computed from this ledger, never entered, and every dimension has to be able to name the rows it came from.
A feed can measure itself before the user has finished scrolling. This product cannot know whether it worked until a quarter has passed — and in most cases never finds out. Every structural decision below is a consequence of that one asymmetry.

Scoping out loud

Scope first, because it is scored and most candidates skip it. Out of scope, stated explicitly: the ranking models themselves (treated as services that return scored items with reason codes attached), the messaging transport, the identity and authentication layer, the résumé parser, and the content moderation pipeline. In scope: how a surfaced item becomes an auditable row at ten billion a day, how an outcome that lands four months later is credited back without corrupting the immutable spine, how a member's declared intent gates collection rather than display, how a reputation score stays reproducible under scrutiny, and how a challenger ranker is evaluated without running an experiment on somebody's livelihood.

Then the envelope math, volunteered rather than extracted. LinkedIn-shaped numbers:

QuantityEstimateConsequence
Members / monthly active≈ 1 B / 310 MPersona dimension is large but slow-moving; the facts are what scale
Surfaced items / day≈ 10 B≈ 116 K/s average — the spine, and every row carries its reason codes
Interactions / day≈ 900 M~9% of surfaced items get a reaction; cheap, fast, explicitly not the objective
Job applications / day≈ 10 MThe last event before the platform loses sight of the process
Confirmed placements / month≈ 200 KThe actual objective — and the denominator problem below
Label density≈ 7 × 10⁻⁷One confirmed placement per ~1.4 M surfaced items. The row that shapes the architecture
Surfaced → placement lag47 d medianp95 is 128 days — hence a 120-day window, and an interval join rather than an equality
Outcomes never observed≈ 60%Most hires happen off-platform; the label is missing-not-at-random
Intent-mode changes / day≈ 4 MSCD2 on the persona, and it gates ingestion at event time
Hottest requisition≈ 40 K / 48 hA viral posting takes 40 K applications in two days; partition by applicant, never by requisition

Notice which row does the architectural work. The ten-billion firehose sets the bill and the partitioning, and the viral requisition sets the write key — both are familiar problems and neither is what this question is about. The row that dictates the shape of the design is the label density: seven in ten million. At that sparsity, a model trained on the objective directly would see almost no positive examples, and a dashboard reporting the objective daily would be reporting noise. Everything that follows — the interaction grain kept as a leading indicator, the attribution bridge that can be rebuilt when the definition improves, the shadow-ranking table, the honest observability tile that reports how much of the label you are missing — exists because the thing you are optimizing for is nearly invisible.

◆ Companion pieces

This scenario is the data-platform working-through of a product argument made at length in “The Feed Is Winning. Your Career Isn’t.” — which asks what a professional network optimized for outcomes rather than attention would actually look like on screen.

There is a working desktop prototype of that product at paddyspeaks.com/careeros. The reason codes, the eight-dimension reputation panel and the “not looking means not collected” rail in this schema are all visible there as interface.


§ 02 — DATA FLOWTwo clocks in one building

The serving path writes a surfaced row in milliseconds with its explanation already attached. The outcome path collects sparse, late, half-external truth for the next four months and stitches it back through a bridge that can be rebuilt from scratch. Between them sits the consent gate, which is the only component in the picture that is allowed to refuse to write.

SERVING CLOCK · ~116K SURFACED/S · MILLISECONDS · IMMUTABLE OUTCOME CLOCK · SPARSE · UP TO 120 DAYS · RE-RUNNABLE MEMBER persona = person × intent mode CONSENT GATE reads intent SCD2 DROPS job-search signal when "not looking" RANK + EXPLAIN model_id · score reason_codes[] not_used[] EVENT LOG key: hash(persona_id) never hash(requisition) a viral requisition spreads fct_surfaced the spine · immutable fct_interaction leading indicator fct_shadow challenger · suppresses none ◆ the gate refuses to write — it does not filter later PLATFORM EVENTS reply · screen · offer SELF-REPORTED "I started a new role" EMPLOYER-VERIFIED ATS callback · rare OUTCOME STITCHER observed_via stamped dedupe across 3 sources no source overwrites another fct_outcome sparse · late · append-only never edits the spine brg_outcome_attribution 120-day interval join attribution_run_id rebuildable opinion RANKER training labels TRUST MART recruiter records REPUTATION recomputed daily EVIDENCE LEDGER claims + confirmations surfaced rows, 120 days back SOLID — event flow · DASHED — the late join · The spine is written once and never edited; only the bridge is allowed to change its mind.
FIG. 1 — Two clocks. The serving path writes an explanation-bearing row in milliseconds and never touches it again. The outcome path spends four months assembling a sparse, three-source truth and connects it back through a bridge carrying a run id, so improving the definition of “this caused that” is a re-run rather than a migration.

Four properties of this picture do most of the interview's work. First, the consent gate sits before the log, not after it. When a member's intent mode is “not looking”, job-search signal is never written — not written-then-hidden, not written-then-filtered. That placement is the difference between a promise and a checkbox, and it is checkable: an anti-join over the spine should return zero rows for those persona-days, and that query belongs in the test suite. Second, the reason codes are produced by the ranker and written onto the surfaced row at serve time. An explanation reconstructed later from a re-scored model is not an explanation of what happened; it is a plausible story about a different ranking. Third, the outcome never edits the spine. A placement discovered in November does not go back and update an August row — it lands in its own fact and is connected through a bridge, so the August row still says exactly what the system believed in August. Fourth, the shadow table records what the challenger model would have surfaced, alongside what the champion actually did, suppressing nothing.

The Failure Philosophy, In One Rule

Write what you knew, when you knew it; discover the consequence later; never let the discovery rewrite the knowledge. The spine is immutable because it is a record of a decision, and decisions do not improve retroactively. The outcome is append-only because a hire that falls through in week two is a second event, not a correction of the first. And the attribution between them is versioned because the definition of “this recommendation led to that job” is genuinely contested — marketing will want first-touch, the ranking team will want last-touch, and the honest answer is that you store the surfaced rows and the outcomes and let the run id decide, so the argument is settled by re-running a job rather than by rebuilding a warehouse.


§ 03 — DATA MODELThe spine, the late fact, and the bridge that can change its mind

Four facts and three dimensions. The surfaced row carries its own explanation. The outcome row carries its own provenance. The bridge between them carries a run id. And the persona dimension is SCD2 not because titles change — though they do — but because the consent scope changes, and every row in the system has to be answerable against the scope that was in force when it was written.

The spine — one row per thing shown, with its reason

This is the most-written table in the design and the one that makes the product's central promise keepable. Alongside the usual serving context — model id, predicted score, slot — it carries two arrays that most recommendation schemas do not have: reason_codes, the ranked inputs that would be shown under “Why this?”, and not_used_codes, the inputs the product publicly commits to not ranking on. Storing the negative space sounds like decoration until an auditor asks you to prove that a role recommendation did not use an inferred demographic, and you can answer with a column instead of a deposition.

DDL · THE SPINE — SURFACED FACT
-- The "surfaced" grain. ~10B rows/day. Partitioned by surfaced_ts (hour), -- clustered on persona_id. Immutable: no UPDATE path exists in any writer. -- consent_scope_id is frozen here so every row can be audited against the -- consent that was actually in force when it was written. CREATE TABLE fct_surfaced ( surfaced_id BIGINT PRIMARY KEY, -- snowflake: time-ordered persona_id BIGINT NOT NULL REFERENCES dim_persona_intent, member_id BIGINT NOT NULL, -- the human behind the persona item_type TEXT NOT NULL CHECK (item_type IN ('role','person','event','post','community')), item_id BIGINT NOT NULL, surface TEXT NOT NULL CHECK (surface IN ('briefing','jobs','network','feed','search')), slot SMALLINT NOT NULL, -- position bias lives here ranking_model_id BIGINT NOT NULL REFERENCES dim_ranking_models, predicted_outcome_score NUMERIC(8,6) NOT NULL, -- P(professional outcome), frozen reason_codes TEXT[] NOT NULL, -- ★ the "Why this?" payload, at serve time reason_weights NUMERIC(5,4)[] NOT NULL, -- parallel array; primary vs supporting not_used_codes TEXT[] NOT NULL, -- ★ the negative space, provable in a query confidence TEXT NOT NULL CHECK (confidence IN ('high','medium','low')), consent_scope_id BIGINT NOT NULL REFERENCES dim_consent_scope, surfaced_ts TIMESTAMPTZ NOT NULL, CONSTRAINT reason_arity CHECK (cardinality(reason_codes) = cardinality(reason_weights)), CONSTRAINT reason_present CHECK (cardinality(reason_codes) > 0) -- ★ nothing unexplained ships ); CREATE INDEX idx_surf_persona ON fct_surfaced (persona_id, surfaced_ts DESC); CREATE INDEX idx_surf_item ON fct_surfaced (item_type, item_id, surfaced_ts DESC); CREATE INDEX idx_surf_model ON fct_surfaced (ranking_model_id, surfaced_ts DESC);

The reason_present constraint is worth defending out loud, because it is the schema enforcing a product promise. If the ranker cannot say why it surfaced something, the row does not get written and the item does not get shown. That is a strong commitment and an interviewer will push on it — what about a cold-start recommendation with no meaningful signal? The answer is that “insufficient evidence to explain” is itself a reason code with a low confidence, and an item carrying it is shown in a labelled exploration slot rather than smuggled into the main ranking pretending to be a match.

The late fact — an outcome, and where it came from

The outcome table is small, slow, and disproportionately important. Its distinguishing column is observed_via: platform-observed events are cheap and trustworthy but cover only the early funnel; self-reported outcomes cover the part that matters most and are unverifiable; employer-verified outcomes are rare and authoritative. Collapsing those three into one boolean would be the single most damaging simplification available here, because every downstream metric needs to state which kind of evidence it rests on.

DDL · THE LATE FACT — OUTCOME
-- Sparse (~200K placements/month against 300B surfaced rows), late (median -- 47 days), and often external. Append-only: an offer that later falls through -- is a NEW row with outcome_type='withdrawn', never an UPDATE of the offer. CREATE TABLE fct_outcome ( outcome_id BIGINT PRIMARY KEY, member_id BIGINT NOT NULL, -- the person it happened to counterparty_id BIGINT, -- recruiter, hiring manager, mentor outcome_type TEXT NOT NULL CHECK (outcome_type IN ( 'reply_substantive','screen','interview','offer', 'placement','withdrawn','mentorship_confirmed', 'introduction_made','collaboration_started')), item_type TEXT, -- what it was about, when known item_id BIGINT, observed_via TEXT NOT NULL CHECK (observed_via IN ( 'platform','self_reported','employer_verified')), -- ★ both_sides_confirmed BOOLEAN NOT NULL DEFAULT FALSE, -- mentorship needs two outcome_ts TIMESTAMPTZ NOT NULL, -- when it happened recorded_ts TIMESTAMPTZ NOT NULL -- when we found out — often much later ); CREATE INDEX idx_out_member ON fct_outcome (member_id, outcome_ts DESC); CREATE INDEX idx_out_type ON fct_outcome (outcome_type, outcome_ts DESC);

Two timestamps, and candidates who only write one lose a point. outcome_ts is when the thing happened; recorded_ts is when the platform learned of it. The gap between them is often weeks, and it is the reason every outcome-based metric must be reported as a cohort that is still filling in. A placement rate for last month is not a number, it is a number and a maturity — and a dashboard that shows the first without the second will show a cliff every time it refreshes.

The bridge — attribution as a versioned opinion

This is the table that makes the design senior. A hire is caused by a chain of surfaced items over four months: the role that appeared in a briefing in June, the hiring manager who surfaced in July, the article that got read before the interview. Which of those gets the credit is a modelling choice, and modelling choices change. So credit does not live on either fact. It lives in a bridge keyed by an attribution_run_id, where a re-run under a new definition produces new rows and leaves the old ones intact — and the two can be compared.

DDL · THE BRIDGE — RE-RUNNABLE ATTRIBUTION
-- Many surfaced rows may share credit for one outcome. Weights sum to 1.0 per -- (outcome_id, attribution_run_id) — enforced by the job and asserted in tests. -- Changing the credit model = a new run_id, not a rewrite. Old runs survive so -- last quarter's reported numbers stay reproducible after the model improves. CREATE TABLE brg_outcome_attribution ( attribution_run_id BIGINT NOT NULL REFERENCES dim_attribution_runs, outcome_id BIGINT NOT NULL REFERENCES fct_outcome, surfaced_id BIGINT NOT NULL REFERENCES fct_surfaced, credit_weight NUMERIC(6,5) NOT NULL CHECK (credit_weight > 0), lag_days SMALLINT NOT NULL, -- outcome_ts - surfaced_ts touch_role TEXT NOT NULL CHECK (touch_role IN ( 'first','assist','last')), PRIMARY KEY (attribution_run_id, outcome_id, surfaced_id) ); CREATE TABLE dim_attribution_runs ( attribution_run_id BIGINT PRIMARY KEY, model_name TEXT NOT NULL, -- 'position_decay_v2' window_days SMALLINT NOT NULL, -- 120 definition_notes TEXT NOT NULL, -- what changed and why is_current BOOLEAN NOT NULL DEFAULT FALSE, run_ts TIMESTAMPTZ NOT NULL );

The persona dimension — where consent actually lives

A persona is (member × intent mode), and it is SCD2. Alex Chen job-hunting in March and Alex Chen not-looking in September are two persona rows for one member, with different consent scopes, different collected signals, and metrics that must never be summed across the boundary. This is the dimension that gates ingestion, so its valid_from / valid_to are not a reporting convenience — they are the authority a writer consults before it is allowed to persist anything.

DDL · PERSONA (SCD2) + CONSENT SCOPE + RANKING MODELS
CREATE TABLE dim_persona_intent ( persona_id BIGINT PRIMARY KEY, member_id BIGINT NOT NULL, intent_mode TEXT NOT NULL CHECK (intent_mode IN ( 'job_hunting','learning','networking','mentoring', 'building','exploring','hiring','recruiting')), consent_scope_id BIGINT NOT NULL REFERENCES dim_consent_scope, headline TEXT NOT NULL, employer_id BIGINT, valid_from TIMESTAMPTZ NOT NULL, valid_to TIMESTAMPTZ NOT NULL DEFAULT '9999-12-31', is_current BOOLEAN NOT NULL ); CREATE INDEX idx_persona_asof ON dim_persona_intent (member_id, valid_from, valid_to); -- The scope is a set of signal families the member has agreed may be COLLECTED. -- 'not looking' is not a display preference; it removes job_search from this set, -- and the ingest gate reads it before every write. CREATE TABLE dim_consent_scope ( consent_scope_id BIGINT PRIMARY KEY, label TEXT NOT NULL, -- 'open_to_roles' | 'not_looking' | ... collects TEXT[] NOT NULL, -- {job_search, learning, mentoring, ...} visible_to TEXT[] NOT NULL, -- {verified_recruiters, target_employers} excludes_employer BOOLEAN NOT NULL DEFAULT TRUE -- never your own employer ); CREATE TABLE dim_ranking_models ( ranking_model_id BIGINT PRIMARY KEY, model_semver TEXT NOT NULL, objective TEXT NOT NULL CHECK (objective IN ( 'outcome','engagement')), -- ★ the audit column role TEXT NOT NULL CHECK (role IN ('champion','shadow')), deployed_pct NUMERIC(5,2) NOT NULL, valid_from TIMESTAMPTZ NOT NULL, valid_to TIMESTAMPTZ NOT NULL DEFAULT '9999-12-31' );

The objective column on the model dimension is a one-word governance mechanism, and it is worth pointing at explicitly in an interview. The product's whole claim is that it ranks on professional outcomes rather than attention. A model registry that records which objective each deployed ranker was trained against turns that claim into something you can query: show me the share of surfaced rows in the last quarter served by a model whose objective was engagement. If that number drifts upward over eighteen months, the thesis is eroding, and the schema noticed before the strategy deck did.

The evidence ledger, and the rollups

Reputation is computed, never stored as an input. The ledger holds claims and the confirmations attached to them; the daily mart holds the computed dimension scores together with the scoring_model_id and a hash of the evidence set they were computed from, so any score can be recomputed and diffed against what was displayed. A number a member cannot interrogate is a rumour with a font.

DDL · EVIDENCE LEDGER + REPUTATION MART
CREATE TABLE fct_evidence_claim ( claim_id BIGINT PRIMARY KEY, member_id BIGINT NOT NULL, dimension TEXT NOT NULL, -- 'mentorship' | 'technical_credibility' | ... claim_text TEXT NOT NULL, artefact_url TEXT, -- the thing someone else can read claimed_ts TIMESTAMPTZ NOT NULL ); -- A claim with no confirmation is visible to its owner and to nobody else, -- and contributes ZERO to the score. Self-assertion does not count itself. CREATE TABLE fct_evidence_confirmation ( confirmation_id BIGINT PRIMARY KEY, claim_id BIGINT NOT NULL REFERENCES fct_evidence_claim, confirmed_by BIGINT NOT NULL, -- a named human, not a reaction relationship TEXT NOT NULL, -- 'mentee' | 'reviewer' | 'employer' confirmed_ts TIMESTAMPTZ NOT NULL, CONSTRAINT no_self_confirm CHECK (TRUE) -- enforced in the job: confirmed_by != owner ); CREATE TABLE mart_reputation_daily ( member_id BIGINT NOT NULL, as_of_date DATE NOT NULL, dimension TEXT NOT NULL, score SMALLINT NOT NULL CHECK (score BETWEEN 0 AND 100), is_provisional BOOLEAN NOT NULL, -- low sample → labelled, not scored confidently is_applicable BOOLEAN NOT NULL, -- n/a beats a zero that reads as failure scoring_model_id BIGINT NOT NULL, evidence_set_hash TEXT NOT NULL, -- ★ recompute + diff = reproducibility test PRIMARY KEY (member_id, as_of_date, dimension) );

§ 04 — THE CORE INVARIANTConsent gates the write; attribution moves at read

One spine, two opposite disciplines. Everything about what was collected is decided once, at write time, and frozen forever. Everything about what it meant is decided at read time, versioned, and allowed to change. Candidates who put both at the same end of the pipeline get a system that either lies about privacy or cannot improve its own definitions.

Take the privacy half first, because it is the one the product markets. A member switches to not looking. The tempting implementation — the one nearly every real platform ships — writes the same events it always did and adds a flag to the read path so job-search signal is hidden from recruiters. It is cheaper, it is reversible, and it means the toggle is a lie: the profile-view scoring, the role-affinity model and the availability inference all keep running, and the member's own employer is one internal query away from what they were promised nobody could see. The honest implementation refuses at the gate. If job_search is not in the persona's collects array at event time, the writer drops the event and increments a counter. Nothing is stored; there is nothing to leak, nothing to subpoena, and nothing to quietly re-enable.

The consequence is one that a good interviewer will immediately probe, so volunteer it: the data is gone, and it does not come back. When the member switches back to job hunting in September, there is no six-month history to warm-start their recommendations, because the six months were never recorded. You take a genuine cold-start penalty in exchange for the promise being true. That trade is the answer — and being able to name its cost is what distinguishes someone who has thought about it from someone reciting a privacy slogan.

A privacy control implemented at read time is a display preference wearing a promise's clothes. If the toggle does not change what gets written, the schema can prove you lied — and one day someone will run that query.CAREER RULE Nº 1 — REFUSE AT THE GATE

Now the attribution half, which pulls in the opposite direction. What caused a hire is not knowable at write time and is not stable afterwards. First-touch flatters discovery surfaces; last-touch flatters the jobs page; a position-decay model over a hundred and twenty days is defensible and will still be argued about. Because the argument never ends, the design refuses to commit: the spine records what was surfaced and the outcome fact records what happened, and the credit connecting them lives in a bridge stamped with a run id. Improving the model is a job that writes new rows. Last quarter's board numbers remain reproducible because the run that produced them still exists.

The two disciplines are complementary, and stating the pairing out loud is the strongest single sentence available in this interview: collection is decided once and never revisited; interpretation is revisited forever and never destructive. A system that gets these the wrong way round — flexible collection, frozen interpretation — is precisely the system that quietly harvests everything and can never explain any of it.

The Counterfactual You Are Not Allowed To Run

A feed measures a challenger ranker by withholding the champion's output from a holdout slice. You cannot do that here. Suppressing a strong job match from a real person for four months to measure lift is an experiment on someone's livelihood, and “we had a holdout” is not a defence anybody wants to read in a deposition. The design answer is shadow ranking: run the challenger over the same request, log what it would have surfaced into fct_shadow_surfaced, and show the member the champion's output anyway. You lose clean randomization and you gain the ability to sleep — and you recover most of the statistical power through the overlap set, comparing outcomes on items both models chose against the ones only one of them did. Say the limitation out loud: shadow evaluation is biased by the champion's own exposure, and the honest correction is a small labelled exploration slot, disclosed to the member as exploration, rather than a silent suppression.


§ 05 — INGESTION & STREAMSPython at the gate and on the long tail

Three programs carry the system. The consent-gated writer that refuses to persist what it was not permitted to collect, the outcome stitcher that reconciles three disagreeing sources without letting any of them overwrite another, and the attribution runner that spreads credit backwards across a hundred and twenty days and stamps a version on its own opinion.

1 · The ingest gate — refuse, then count the refusal

This sits on the serving hot path, ahead of the log. It resolves the persona's consent scope from a cache of the SCD2 dimension, drops any event whose signal family is not in collects, and — the part that turns a policy into an operable system — records that it dropped one. A suppression counter is what lets the dashboard show that the gate is alive; a gate nobody measures is indistinguishable from a gate that was quietly removed in a refactor eighteen months ago.

PYTHON · INGEST GATE — DROP AT THE WRITE, NEVER FILTER AT THE READ
import hashlib, time N_PARTITIONS = 4096 SIGNAL_FAMILY = { # event kind → consent family "role_surfaced": "job_search", "role_saved": "job_search", "recruiter_viewed": "job_search", "article_surfaced": "learning", "question_answered": "mentoring", } class ConsentGatedWriter: """The only component permitted to refuse. If the persona's consent scope does not include this event's signal family, the event is DROPPED — not written-and-hidden, not written-and-filtered-later. The member was told nothing is collected, so nothing is collected. The cost is real: when they switch back to job hunting there is no history to warm-start from.""" def __init__(self, log, personas, metrics): self.log, self.personas, self.metrics = log, personas, metrics def write(self, member_id, event) -> bool: # Resolve the persona in force AT EVENT TIME, not the current one — a # late-arriving event must be judged by the consent that was live then. persona = self.personas.as_of(member_id, event.ts) family = SIGNAL_FAMILY.get(event.kind) if family and family not in persona.consent.collects: # ★ The refusal. Counted so the gate is observable in production. self.metrics.incr("ingest.suppressed", tags={"family": family}) return False record = { "persona_id": persona.id, "member_id": member_id, "consent_scope_id": persona.consent.id, # frozen for the audit **event.payload, } self.log.emit(self._partition(member_id), record) return True def _partition(self, member_id: int) -> int: # Key on the MEMBER, never the requisition: a viral posting takes 40K # applications in 48h from 40K different people, so a member key spreads # that spike while a requisition key would stack it into one partition. h = hashlib.blake2b(f"m:{member_id}".encode(), digest_size=8) return int.from_bytes(h.digest()) % N_PARTITIONS

One carve-out, always stated: the gate resolves consent as of the event timestamp, not as of now. Events arrive out of order and sometimes hours late, and a member who switched to not looking at noon must have their 11:55 events written and their 12:05 events dropped. Using the current persona would either retroactively suppress data that was legitimately collected or, far worse, admit data collected after the promise took effect.

2 · The outcome stitcher — three sources, none of which wins

Platform events, self-reports and employer verifications all describe the same hire and none of them agrees on the date. The naive implementation picks a precedence order and overwrites; the correct one keeps all three as separate rows carrying their provenance and marks a canonical view, so a metric can always state which evidence it is standing on and an analyst can always ask how the answer changes if self-reports are excluded.

PYTHON · OUTCOME STITCHER — PROVENANCE PRESERVED, NOTHING OVERWRITTEN
from datetime import timedelta TRUST = {"employer_verified": 3, "platform": 2, "self_reported": 1} async def stitch_outcome(store, ev) -> None: """Every source appends its own row. We never let a higher-trust source delete a lower-trust one — the disagreement is itself a finding, and the self-report may well be the only record that exists for the 60% of hires the platform never sees. We only mark which row is canonical.""" await store.insert_outcome( outcome_id = ev.id, member_id = ev.member_id, outcome_type = ev.type, observed_via = ev.source, # platform | self_reported | employer_verified outcome_ts = ev.happened_at, # when it happened recorded_ts = ev.received_at) # when we found out — often weeks later # Cluster rows describing the same real-world event: same member, same type, # within a 14-day window. Wider than you would like, because self-reports # are rounded to the month and ATS callbacks lag payroll. siblings = await store.find_outcomes( member_id = ev.member_id, type = ev.type, between = (ev.happened_at - timedelta(days=14), ev.happened_at + timedelta(days=14))) canonical = max(siblings, key=lambda o: (TRUST[o.observed_via], -o.recorded_ts)) await store.mark_canonical(canonical.outcome_id, siblings) # Note: the non-canonical rows STAY. "Employer says March, member says # February" is a data-quality signal, not a conflict to be resolved away.

3 · The attribution runner — credit, backwards, with a version on it

This is the batch job that connects the two clocks. For each outcome it looks back across the attribution window at everything the product surfaced to that member about that item, and spreads credit with a decay that respects both recency and position. It writes into the bridge under a fresh run id and never touches an existing one. The interesting detail is what it does when it finds nothing: an unattributed outcome is recorded as such rather than silently dropped, because the share of outcomes the product cannot claim credit for is one of the most honest numbers in the entire system.

PYTHON · ATTRIBUTION RUNNER — 120-DAY LOOKBACK, POSITION-DECAYED, VERSIONED
def attribute(outcome, surfaced_rows, run) -> list[dict]: """Spread credit for ONE outcome across the surfaced rows that plausibly led to it. Called per outcome; `surfaced_rows` is everything shown to this member about this item inside run.window_days before outcome_ts. Writes under run.id and never mutates a previous run — so the number the board saw last quarter is still exactly reproducible after we improve the credit model this quarter.""" if not surfaced_rows: # ★ The honest branch. ~60% of placements land here because the hire # happened off-platform. Recorded, never discarded: "outcomes we # cannot claim" is a headline metric, not an error condition. return [{"attribution_run_id": run.id, "outcome_id": outcome.id, "surfaced_id": None, "credit_weight": 1.0, "touch_role": "unattributed", "lag_days": None}] rows = sorted(surfaced_rows, key=lambda s: s.surfaced_ts) raw = [] for i, s in enumerate(rows): lag = (outcome.outcome_ts - s.surfaced_ts).days # Recency decay over the window, damped by position: something shown # at slot 1 of a briefing did more work than slot 40 of a scroll. recency = 0.5 ** (lag / run.half_life_days) position = 1.0 / (1.0 + 0.06 * s.slot) raw.append((s, recency * position)) total = sum(w for _, w in raw) or 1.0 return [{ "attribution_run_id": run.id, "outcome_id": outcome.id, "surfaced_id": s.surfaced_id, "credit_weight": w / total, # sums to 1.0 per outcome "lag_days": (outcome.outcome_ts - s.surfaced_ts).days, "touch_role": "first" if s is rows[0] else "last" if s is rows[-1] else "assist", } for s, w in raw]

§ 06 — AGGREGATIONCohorts that are still filling in

Every aggregate in this system has a problem no feed metric has: it is incomplete in a way that changes for months after the period closes. The aggregation layer's real job is not speed, it is honesty — reporting outcome rates as maturing cohorts, recomputing reputation from the evidence ledger rather than incrementing it, and refusing to publish a rate whose denominator can be gamed.

Start with the maturity problem, because it is the one that embarrasses teams in public. Placement rate for June, computed on the first of July, will be roughly a third of placement rate for June computed on the first of November — not because anything improved, but because the outcomes had not arrived yet. A dashboard that reports the first number will show a catastrophic drop every month and a miraculous recovery every quarter, and the ranking team will chase both. The fix is structural: every outcome-based aggregate is stored per cohort and per observation age, so the number June had at thirty days can be compared against the number May had at thirty days. You never compare a fresh cohort to a mature one, and the mart makes that comparison the easy one to write.

PYTHON · MATURING-COHORT ROLLUP — RATE BY OBSERVATION AGE
# mart_outcome_cohort_daily: for each (surfaced cohort week × observation age # in days), the attributed outcome rate seen SO FAR. Never a single "rate". # Comparing two cohorts is only legitimate at equal observation age. surfaced = (fct_surfaced .filter(lambda s: s.item_type == "role") .key_by(lambda s: (week_of(s.surfaced_ts), s.ranking_model_id)) .aggregate(surfaced_n=count(), members=hll_sketch("member_id"))) # distinct people, not rows attributed = (brg_outcome_attribution .filter(lambda b: b.attribution_run_id == CURRENT_RUN) .join(fct_outcome, on="outcome_id") .join(fct_surfaced, on="surfaced_id") .key_by(lambda r: (week_of(r.surfaced_ts), r.ranking_model_id, observation_age_days(r))) # ★ the second axis .aggregate(credited=sum("credit_weight"), # fractional, sums to 1/outcome placements=count_where("outcome_type == 'placement'"))) def to_cohort_row(key, s, a): week, model, age = key return { "cohort_week": week, "ranking_model_id": model, "observation_age": age, # 7, 14, 30, 60, 90, 120 "surfaced_n": s.surfaced_n, "credited_outcomes": a.credited, "rate_per_100k": 1e5 * a.credited / s.surfaced_n, "is_mature": age >= 120, # ★ never report as final before this }

Then the denominator problem, which is where the trust metrics live and where an interviewer can profitably push. The product promises to show candidates a recruiter's response rate before they reply — the recruiter's own scorecard, visible to the person deciding whether to invest hope in a conversation. That number is only meaningful if the denominator cannot be curated. If “messages received” is what the recruiter's tooling chooses to mark as received, the rate is theatre. So the denominator is defined on the platform side of the boundary — first contacts that were delivered — and the numerator counts any terminal state including rejection, because a fast no is a good outcome and a system that penalizes it teaches recruiters to ghost.

Reputation follows the same discipline in a different key. The score is recomputed from the evidence ledger every day rather than incremented, because an incremented score drifts away from its evidence and can never be reconciled back. Recomputation is affordable precisely because the evidence grain is small — claims and confirmations number in the millions per day, not billions — and it buys the property the product is selling: any member can ask which rows produced their number, and the answer is a join rather than an apology. Low-sample dimensions are marked is_provisional rather than scored confidently, and dimensions that do not apply to someone's work are marked is_applicable = false rather than scored zero, because a zero reads as a failing grade and a blank reads as the truth.

Report a rate with its maturity or do not report it. Define a denominator on the side of the boundary the measured party does not control. Recompute a score from its evidence rather than incrementing it away from the evidence. Three rules, one principle: the number must survive being asked where it came from.CAREER RULE Nº 2 — EVERY NUMBER NAMES ITS SOURCE

§ 07 — ANALYTICS SQLInterrogating a system that mostly cannot see its own results

Four queries, each carrying a pattern worth having. The interval join that credits a hire back across four months, the anti-join on an absent event that measures ghosting, the as-of replay that proves a reputation score reproducible, and the assertion query whose entire purpose is to return nothing.

Outcome lift per ranking model — the interval join

The query the whole schema exists to make possible. Because ranking_model_id rides every surfaced row and credit lives in a versioned bridge, comparing an outcome-trained ranker against an engagement-trained one is a GROUP BY — not a bespoke experiment pipeline. The senior framing is to report both the outcome rate and the interaction rate, because the interesting and uncomfortable result is a model that wins on clicks and loses on hires, which is exactly the thesis under test.

SQL · OUTCOME RATE BY MODEL — 120-DAY WINDOW, COHORT-MATURE ONLY
WITH cohort AS ( SELECT s.surfaced_id, s.member_id, s.slot, s.surfaced_ts, m.model_semver, m.objective FROM fct_surfaced s JOIN dim_ranking_models m ON m.ranking_model_id = s.ranking_model_id WHERE s.item_type = 'role' AND m.role = 'champion' -- Only cohorts old enough to have stopped filling in. Comparing a -- 3-week-old cohort to a 6-month-old one is the classic own goal. AND s.surfaced_ts < CURRENT_DATE - INTERVAL '120 days' AND s.surfaced_ts >= CURRENT_DATE - INTERVAL '300 days' ), credited AS ( SELECT b.surfaced_id, sum(b.credit_weight) FILTER ( WHERE o.outcome_type = 'placement') AS placement_credit, sum(b.credit_weight) FILTER ( WHERE o.outcome_type IN ('interview','offer')) AS late_funnel_credit FROM brg_outcome_attribution b JOIN fct_outcome o ON o.outcome_id = b.outcome_id JOIN dim_attribution_runs r ON r.attribution_run_id = b.attribution_run_id WHERE r.is_current -- pin the opinion being reported AND b.lag_days <= r.window_days GROUP BY b.surfaced_id ) SELECT c.model_semver, c.objective, count(*) AS surfaced_rows, approx_count_distinct(c.member_id) AS members, round(1e5 * coalesce(sum(k.placement_credit), 0) / nullif(count(*), 0), 3) AS placements_per_100k, round(1e5 * coalesce(sum(k.late_funnel_credit), 0) / nullif(count(*), 0), 3) AS late_funnel_per_100k, round(100.0 * count(i.interaction_id) / nullif(count(*), 0), 3) AS interaction_rate_pct FROM cohort c LEFT JOIN credited k ON k.surfaced_id = c.surfaced_id LEFT JOIN fct_interaction i ON i.surfaced_id = c.surfaced_id GROUP BY c.model_semver, c.objective ORDER BY placements_per_100k DESC; -- The result that matters: an 'engagement' model can top interaction_rate_pct -- and sit at the bottom on placements_per_100k. That gap is the whole argument.

Recruiter ghosting — the anti-join on an event that never happened

The trust metric candidates can see before they reply. Its craft is entirely in the definition: the denominator is delivered first contacts, which the recruiter does not control, and a rejection counts as a response, because a system that treats a fast no as a failure will manufacture silence. Ghosting is the absence of any terminal state within fourteen days — an absence, which means the pattern is a NOT EXISTS. Nothing happened, and the nothing is the measurement.

SQL · RECRUITER TRUST — RESPONSE, GHOSTING, ROLE ACCURACY
WITH first_contact AS ( SELECT m.message_id, m.recruiter_id, m.member_id, m.sent_ts, m.role_id FROM fct_message m WHERE m.is_first_contact AND m.delivery_state = 'delivered' -- ★ platform-side denominator AND m.sent_ts >= CURRENT_DATE - INTERVAL '90 days' AND m.sent_ts < CURRENT_DATE - INTERVAL '14 days' -- room to resolve ), resolved AS ( SELECT f.*, min(r.replied_ts) FILTER (WHERE r.direction = 'member_to_recruiter') AS member_replied_ts, min(t.terminal_ts) AS recruiter_closed_ts FROM first_contact f LEFT JOIN fct_message r ON r.thread_id = f.message_id -- A terminal state is ANY of: advanced, rejected, or withdrawn. A prompt -- rejection is a good outcome; counting it against the recruiter would -- teach exactly the silence this metric exists to punish. LEFT JOIN fct_thread_terminal t ON t.thread_id = f.message_id GROUP BY f.message_id, f.recruiter_id, f.member_id, f.sent_ts, f.role_id ) SELECT recruiter_id, count(*) AS first_contacts, round(100.0 * count(member_replied_ts) / nullif(count(*), 0), 1) AS member_reply_rate_pct, -- ★ Ghosting: the member engaged and NOTHING came back within 14 days. round(100.0 * count(*) FILTER ( WHERE member_replied_ts IS NOT NULL AND (recruiter_closed_ts IS NULL OR recruiter_closed_ts > member_replied_ts + INTERVAL '14 days')) / nullif(count(*) FILTER (WHERE member_replied_ts IS NOT NULL), 0), 1) AS ghosting_rate_pct, round(percentile_cont(0.5) WITHIN GROUP ( ORDER BY extract(epoch FROM recruiter_closed_ts - member_replied_ts)/3600) , 1) AS median_response_hours, -- Role accuracy: placements whose role matches the role that was pitched. round(100.0 * count(*) FILTER (WHERE EXISTS ( SELECT 1 FROM fct_outcome o WHERE o.member_id = resolved.member_id AND o.outcome_type = 'placement' AND o.item_id = resolved.role_id)) / nullif(count(*) FILTER (WHERE member_replied_ts IS NOT NULL), 0), 1) AS role_accuracy_pct FROM resolved GROUP BY recruiter_id HAVING count(*) >= 20 -- below this, publish nothing: small samples defame ORDER BY ghosting_rate_pct ASC;

Reputation reproducibility — as-of replay and diff

The query that turns “every score is traceable” from a slogan into a test. Recompute a dimension from the evidence ledger as it stood on a past date — using only confirmations that existed then, under the scoring model that was live then — and diff it against what was actually displayed. Any row that comes back is either a bug in the scoring job or a silent backfill someone did not disclose, and both are things you want to find before a member does.

SQL · SCORE REPLAY — RECOMPUTE AS-OF, THEN DIFF THE DISPLAYED VALUE
WITH params AS (SELECT DATE '2026-06-30' AS as_of, 'mentorship' AS dim), -- The evidence that existed on that date — no later confirmations allowed in. evidence_asof AS ( SELECT c.member_id, count(DISTINCT c.claim_id) AS claims, count(DISTINCT f.claim_id) AS confirmed_claims, count(DISTINCT f.confirmed_by) AS distinct_confirmers FROM fct_evidence_claim c CROSS JOIN params p LEFT JOIN fct_evidence_confirmation f ON f.claim_id = c.claim_id AND f.confirmed_ts <= p.as_of + INTERVAL '1 day' AND f.confirmed_by <> c.member_id -- no self-confirmation, ever WHERE c.dimension = p.dim AND c.claimed_ts <= p.as_of + INTERVAL '1 day' GROUP BY c.member_id ), -- Replay the scoring function that was LIVE on that date, not today's. replayed AS ( SELECT e.member_id, score_dimension(p.dim, e.confirmed_claims, e.distinct_confirmers, sm.scoring_model_id) AS replayed_score, e.confirmed_claims < 3 AS should_be_provisional FROM evidence_asof e CROSS JOIN params p JOIN dim_scoring_models sm ON p.as_of >= sm.valid_from AND p.as_of < sm.valid_to ) SELECT r.member_id, m.score AS displayed_score, r.replayed_score, m.score - r.replayed_score AS drift, m.is_provisional, r.should_be_provisional FROM replayed r CROSS JOIN params p JOIN mart_reputation_daily m ON m.member_id = r.member_id AND m.as_of_date = p.as_of AND m.dimension = p.dim WHERE m.score IS DISTINCT FROM r.replayed_score OR m.is_provisional IS DISTINCT FROM r.should_be_provisional ORDER BY abs(m.score - r.replayed_score) DESC; -- A healthy system returns zero rows. Anything here is a scoring bug or an -- undisclosed backfill — and a member is entitled to an answer about both.

The consent-leak audit — an assertion written as a query

The shortest query on the page and the one that decides whether the product's central promise is real. For every persona-day where the consent scope excluded job-search collection, there must be no job-search row on the spine. It is an anti-join, it belongs in the nightly test suite as an assertion rather than a report, and its correct output is nothing at all.

SQL · LEAK AUDIT — MUST RETURN ZERO ROWS
SELECT s.persona_id, p.intent_mode, cs.label AS consent_label, count(*) AS leaked_rows, min(s.surfaced_ts) AS first_leak, max(s.surfaced_ts) AS last_leak FROM fct_surfaced s JOIN dim_persona_intent p ON p.persona_id = s.persona_id JOIN dim_consent_scope cs ON cs.consent_scope_id = s.consent_scope_id WHERE s.item_type = 'role' -- a job-search signal by definition AND NOT ('job_search' = ANY(cs.collects)) -- ...under a scope that forbids it AND s.surfaced_ts >= p.valid_from AND s.surfaced_ts < p.valid_to -- judged by the consent in force THEN GROUP BY s.persona_id, p.intent_mode, cs.label; -- Expected result: 0 rows. Wire this as a hard assertion, not a dashboard tile. -- If it ever returns a row, the gate was bypassed and the promise was broken — -- and because consent_scope_id is frozen on the fact, the blast radius is exact.

§ 08 — THE DASHBOARDProving a product that mostly cannot see its own results

A senior design ends with observability, and here observability has an unusual job: it has to report how much of the truth is missing. Four panels — are outcomes actually happening, is the ranker earning them rather than earning clicks, is the trust layer holding, and is the consent gate still alive.

OUTCOME HEALTH
placements per 100k surfaced at a fixed 120-day maturity, median days to outcome, label observability (the share of known outcomes the platform actually saw), and unattributed share — the outcomes the product cannot claim credit for. A rising unattributed share is not a bug; it is the honest measure of how much of a member's career happens elsewhere.
RANKER HEALTH
champion vs shadow outcome lift on the overlap set, reason-code coverage (must be 100% — the schema enforces it, so any dip means a writer is bypassing the constraint), engagement-objective share of surfaced rows, and interaction-outcome divergence — the tile that catches a model winning clicks while losing hires.
TRUST HEALTH
market-wide ghosting rate, median recruiter response, role accuracy, and recruiters below the publication threshold — the ones with too little history to score, who should be shown as unrated rather than flattered by a small sample.
CONSENT HEALTH
leak-audit rows (hard zero, alarmed at one), suppressed events (should be large and stable — a sudden drop means the gate stopped firing), intent-change propagation lag, and reputation replay drift. Three of these four should be boring forever, and the day one stops being boring is the day the promise broke.
Professional Outcomes Ops — Global TUE 09:40 UTC · CHAMPION v4.2 (OUTCOME) · SHADOW v4.3 · ATTRIBUTION RUN 118 · 120-DAY MATURITY
Placements / 100k Surfaced
7.4
Median Days to Outcome
47d
Label Observability
39%
Unattributed Share
61%
Interaction rate vs placement rate, by ranking model — the divergence that is the entire thesis
eng v2.9 eng v3.4 out v4.1 out v4.2 ■ interaction rate ■ placements / 100k
Shadow Lift (overlap)
+9.1%
Reason-Code Coverage
100%
Engagement-Objective Share
18%
Market Ghosting Rate
11%
Leak-Audit Rows
0
Suppressed Events / min
41K
Reputation Replay Drift
0
FIG. 2 — The story an honest outcome product tells about itself: placements per hundred thousand climbing and the shadow challenger ahead on the overlap set, while two amber tiles admit what the system cannot see — only thirty-nine percent of known outcomes were observed on-platform, and sixty-one percent of them cannot be credited to anything the product did.

Read the panels against each other and the dashboard argues the whole thesis on its own. The divergence chart is the centrepiece: the two engagement-trained models on the left top the interaction rate and sit near the bottom on placements, while the outcome-trained models invert exactly that ordering. That is the finding the entire schema was built to be able to produce, and it is only producible because objective is a column on the model dimension and credit is a versioned bridge rather than a hardcoded join. Meanwhile the engagement-objective share sits at eighteen percent and trending up — some surfaces have quietly reverted to a click-trained ranker, which is exactly the eighteen-month erosion the product's own strategy document warns about, caught by a tile rather than a retrospective.

And the two flat zeros at the bottom right are the most important numbers on the board precisely because they are boring. Zero leak-audit rows means the consent gate held every day this quarter. Zero replay drift means every reputation score displayed today can be recomputed from its evidence and lands on the same number. Neither tile will ever be interesting, and both should page someone the instant they stop being zero. Next to them sits the suppressed-events counter at forty-one thousand a minute — the gate visibly doing its job. A team that only alarms on the leak count would not notice the far more likely failure: a refactor that removes the gate entirely, at which point the leak count stays at zero because nothing is being refused, and the suppression counter silently falls off a cliff.


§ 09 — THE RUBRICWhat was actually being tested

Strip the professional-network details away and the question was testing six judgments, every one of which generalizes to any system whose objective function is slow, sparse and partly unobservable — clinical outcomes, education, credit, insurance, anything where the thing you care about arrives long after the thing you did.

LABEL LATENCY
Recognizing that the objective is not available at write time, and refusing to design as if it were. The spine records the decision; the outcome is a separate fact arriving up to four months later; the connection between them is a versioned bridge. A candidate who puts an outcome column on the surfaced row has not understood the question.
ATTRIBUTION AS OPINION
Treating "this recommendation caused that hire" as a rebuildable model with a run id rather than a fact with a foreign key — so improving the definition is a job that writes new rows, and last quarter's reported numbers stay reproducible after the definition improves.
CONSENT AT THE WRITE
Placing the privacy control ahead of the log rather than in front of the reader, accepting the cold-start cost that follows, and being able to name that cost. Then wiring the anti-join that proves it, as an assertion rather than a report.
REPRODUCIBILITY
Computing a member-visible score from an evidence ledger rather than incrementing it, stamping the scoring model and the evidence set, and running a replay-and-diff so any number can be asked where it came from. Unconfirmed claims count for nothing; a dimension that does not apply reads n/a, never zero.
COUNTERFACTUAL ETHICS
Knowing that the standard holdout is unavailable when the treatment is someone's livelihood, choosing shadow ranking instead, and volunteering its bias rather than being caught by it. This is the judgment that most cleanly separates a staff answer from a strong senior one.
MEASUREMENT HONESTY
Reporting rates with their maturity, defining denominators on the side of the boundary the measured party does not control, and putting the share of outcomes the product cannot claim credit for on the dashboard as a headline rather than hiding it in a footnote.
A feed knows within seconds whether it worked. This system waits a quarter, and most of the time never finds out at all. Everything above is the discipline of building something you can still trust under that ignorance — writing down exactly what you knew and when, admitting how much you cannot see, and never letting a later discovery quietly edit an earlier decision.— CLOSING ARGUMENT
← paddyspeaks.com