Lesson 1 of 4 · 60 min

Turn a broad dashboard brief into a finished core

Produce a scoped work-sample plan and a correct result for a small dataset.

A broad work sample often contains more possible features than the available time permits. The first decision is which user question the artifact must answer. A dashboard that supports many filters but displays incorrect totals is less useful than a small page with one trustworthy result. State the user, decision, input, and expected output before choosing components.
Inspect the data directly. Look for missing values, duplicate identifiers, units, timestamps, and records that should be excluded. A chart library cannot decide whether two rows are duplicate events or separate attempts. If the brief is unclear, ask or document a narrow assumption that can be changed. Keep that assumption near the calculation and in the final explanation.
Build one path through data loading, validation, calculation, rendering, and error recovery. Use a small fixture to confirm the result by hand. Then expand to the supplied dataset. This sequence gives you a known reference when a larger result looks surprising. It also creates a useful demonstration early enough to receive feedback.
PostHog's public work-sample guidance emphasizes working core behavior, data understanding, and explained choices. This course uses original scenarios, not a reconstruction of its private assignment. An employer's hiring format is evidence for the skill to practice, not proof that this exact dashboard will appear in an interview.

Worked example

The fictional brief asks for a current-state support dashboard. The immediate user question is which queue has the oldest unresolved ticket. Each input row is a complete ticket snapshot. Ticket ID is entity identity; a source-authoritative snapshot version orders states when an ID repeats. The five initial rows below each have version 1:
code
1id queue   status  ageHours21  billing open    832  product closed  2043  billing open    354  product open    1165  product open    missing
The completed core shows product as the oldest known unresolved queue at 11 hours, billing at 8, and one open ticket with unknown age. It does not treat missing as zero or let the closed ticket dominate. The page includes a visible data-quality note and a retry action for loading failure. A time-range selector can wait because it is not necessary to answer this fixture's question.

Write a decision contract before a component list

A useful scoped brief has a user action, an eligibility rule, an aggregation rule, and a visible uncertainty rule. For this support case, the operator chooses which queue to inspect first. Resolve each ticket's latest valid snapshot across all statuses first, then select currently open tickets. Each queue's known maximum age is displayed, and missing ages remain a visible count. In this initial fixture, billing's ages are complete and its maximum is 8, so product is guaranteed to contain an older unresolved ticket because product already has a known age of 11. Product's exact oldest age remains unknown. If another queue has an unknown age, the complete queue ranking may instead be uncertain.
An acceptance record makes the scope concrete:
code
1User: queue operator2Decision: which unresolved queue to inspect first3Identity/order: resolve latest source-authoritative snapshot per ticket ID4               across all statuses before eligibility5Eligible: resolved current status=open6Unit: hours7Aggregate: maximum known age per queue8Unknown: count separately; never convert missing to zero9Duplicate policy: identical repeated snapshots collapse;10                  different versions resolve under the source ordering contract;11                  same-version conflicts or missing order require review12First result: product 11h, billing 8h, one unknown product age
A single number labelled oldest ticket would overstate the evidence. Use oldest known unresolved age and expose the unknown count. This distinction changes the user's confidence without preventing a useful answer.

Audit the fixture as data

Before implementing, calculate intermediate counts. Five input rows resolve to five current tickets because each ID appears once. Those current tickets contain four open tickets and one closed ticket. Among open tickets, three have known ages and one has unknown age. Billing has two known open ages, 8 and 3; product has one known age, 11, and one unknown. The transformation must resolve identity and latest state over all status rows before applying the open-ticket filter, then compute the maximum and unknown count. Filtering out closed snapshots first can revive an older open state.
StageRow countExpected evidence
Raw fixture5All source rows retained for inspection
Resolved current tickets5Latest valid snapshot selected across all statuses
Eligible open tickets4Resolved closed ticket excluded from metric
Known eligible ages3Missing age not coerced
Queue summaries2Billing 8, product 11 plus unknown count
These counts catch several mistakes. Taking the maximum before filtering produces 20 from the closed ticket. Converting missing to zero makes the output falsely complete. Deduplicating by age rather than ticket ID could merge two different tickets that happen to have the same age. State entity identity and source ordering before eligibility or aggregation.
A second fixture must challenge the transformation order:
code
1id snapshotVersion queue   status ageHours24  1               product open   1134  2               product closed 12
Append the version 2 row to the initial fixture. Resolve ticket 4 to closed before filtering. The six input rows still produce five current tickets, but only three are open: billing tickets 1 and 3, plus product ticket 5 with unknown age. Billing's known maximum is 8. Product has no known open age and one unknown, so the complete queue ranking is uncertain. Filtering open rows first would discard version 2 and incorrectly retain ticket 4's old 11-hour open state.
If the source provides no reliable ordering, do not invent it from array position. A timestamp can establish precedence only when the source contract says it is authoritative and defines ties. Conflicting snapshots with the same version require explicit review rather than an arbitrary winner.

Allocate time around a complete result

Assume an invented ninety-minute limit. Spend ten minutes reading the fixture and writing the contract, twenty-five implementing the transformation and a hand-checked test, twenty-five connecting loading and display, fifteen adding error/retry behavior, and fifteen reviewing the result and preparing the explanation. These are planning estimates, not measured universal timings.
If loading consumes extra time, cut optional charts or filters before cutting the only correct result path. Keep the hand-checkable fixture because it anchors the calculation. If the brief explicitly requires a chart, choose a simple representation of the verified summary rather than spending the entire remaining period on configuration. A task's stated requirement still takes precedence over your preference.
A timebox is not a reason to hide an incomplete boundary. If the sample uses local data, say so. If the network error state is simulated, show how and explain what actual transport behavior remains untested. The finished core should be honest about its scope, not claim a production integration it lacks.

Misconceptions and a transfer exercise

One misconception is that the chart library owns metric correctness. It can render an array correctly while the array represents the wrong population. Another is that missing values are just inconvenient formatting. Their treatment changes the claim the user can draw from the result.
Exercise: use the original five-ticket current-state fixture: product has ages 11 and missing; billing has complete ages 8 and 3. Product is guaranteed to contain the oldest unresolved ticket among these queues, because even its known 11 exceeds billing's complete maximum of 8. The exact oldest age is unknown because product's missing value may exceed 11. Now move that unknown-age ticket to billing while retaining product's known 11. Billing's unknown may exceed 11, so the complete queue ranking becomes uncertain. Award one point for eligibility, one for distinguishing age from ranking, one for the changed-queue case, and one for accurate wording.
Finish with the fixture, contract, and result next to each other. A reviewer should be able to follow the transformation without running a large application. That is the core artifact the rest of the dashboard supports.

Exercise and solution

The interviewer adds a duplicate row for ticket 4 with the same status and age. Define your assumption and result. Under the stated snapshot contract, collapse the identical version-1 delivery while preserving source identity, then resolve current state across all statuses before filtering open tickets. Product's known maximum remains 11. As a second check, append the closed version-2 snapshot above: ticket 4 must no longer contribute. Award one point for the identical-delivery rule, one for unchanged original output, one for resolving the newer closed state before eligibility, and one for flagging same-version conflicts or missing ordering.

Interview probe and wrap-up

What would you cut with thirty minutes remaining? A strong answer preserves the calculation, basic interaction, and error state while cutting optional visual polish. Follow up with a stakeholder requesting a graph. A weak answer implements the graph without a verified aggregation. Scope is a correctness decision as well as a time decision.

Sources

docsPostHog engineering work-sample guidanceposthog.comdocsPostHog technical-screen guidanceposthog.com

Checkpoint

Which label best fits product 11 with an unresolved unknown-age ticket?

AOldest ticket is exactly 11 hours.BAll product tickets are younger than 12 hours.CLargest known unresolved age is 11 hours, with one unknown.DAverage unresolved age is 11 hours.
Sign up free to answer and see why

Checkpoint

Which transformation order matches the metric?

AResolve latest valid snapshots per ticket across all statuses, then filter current open tickets and aggregate known ages and unknown counts.BConvert unknown to zero, then choose the maximum.CDeduplicate all equal ages before filtering.DMaximum over all rows, then remove closed labels.
Sign up free to answer and see why

Checkpoint

Two rows share an ID but conflict, and no ordering field exists. What is defensible?

AChoose whichever array entry occurs last.BCount both as independent tickets.CAverage their ages.DFlag ambiguity and request or document a valid precedence policy.
Sign up free to answer and see why

Checkpoint

The core is correct; fifteen minutes remain. Which priority best supports a reviewable submission?

AHide unknown rows to simplify the demo.BVerify failure/retry behavior and record scope before optional filters.CAdd all filters without checking results.DReplace the working transformation with a new library.
Sign up free to answer and see why

Checkpoint

An unknown-age row moves from product to billing. What changes?

ABoth queues must be removed.BBilling must become the oldest queue.CThe known maximum remains product 11, but complete queue ranking is now uncertain.DUnknown must be treated as zero to preserve ranking.
Sign up free to answer and see why

Can you state the eligible population, identity, aggregation, and uncertainty rules before choosing a dashboard view? State the relevant identifiers, failure boundary, and evidence in your own words before selecting your confidence.

Not yetGetting thereConfident

Sources

Free to read · better with Enzo

Learn it with Enzo

Save your progress, answer the checkpoints, and let Enzo quiz you on what you just read.