Growth Engineer · Data Systems · Remote - Asia

growth data-systems audit

I opened BitMEX's live site, read the tracking implementation in the page bundle, mapped it against the JD, and wrote the blueprint I'd execute in the first 90 days: the path from a user's tap → Segment → Mixpanel / GA4 / AppsFlyer, managed in version control. Anything I couldn't verify from outside is marked as such.

Confirmed live
Segment
CDP - write key in bundle
Confirmed live
GTM + Sentry
tags + error pipeline
JD stack
TF · Node · React
analytics-as-code
The job
Source of Truth
trustworthy event data
BitMEX · crypto-derivatives exchange · est. 2014 · inventor of the perpetual swap · operated by HDR Global Trading
00

Summary

This role isn't "add an analytics tool." It's owning the integrity of the data that Marketing, Growth and Finance make decisions on - at an exchange where a mislabelled or double-counted event can mean misallocated ad spend and a funnel nobody trusts. The JD's own words: be the "Source of Truth," own the "technical DNA of the analytics stack."

A live look at bitmex.com confirms the core is already in place: Twilio Segment as the CDP, Google Tag Manager, and Sentry, with Mixpanel, GA4 and AppsFlyer downstream. So the work isn't greenfield; it's governance, reliability and version control: a typed tracking plan, money events moved server-side, the config in Terraform, attribution wired for mobile, and monitoring that catches a broken pipeline before it reaches a dashboard. This deck covers the stack as it stands, the failure modes specific to a derivatives exchange, and the blueprint and code I'd ship.

01

BitMEX - and why its data is hard

Snapshot

What's happening at BitMEX right now Jun 2026
TradFi / tokenised perps are exploding

BitMEX's tokenised commodity & equity perpetuals reportedly jumped 500%+, with TradFi perps hitting ~$30B+ weekly volume by Q1 2026 (silver, gold, crude). Each new product line is a new funnel and a new set of events to instrument - directly why a data-systems owner is needed now, not later.

Ownership in flux

Reports since Feb 2025 of BitMEX exploring a sale. An analytics stack that's clean, documented and in code is exactly what survives diligence and an ownership transition - a quiet but real argument for this role's mandate.

Public reporting (Messari, Investing.com/Chainwire, RootData); figures approximate - I'd confirm against internal numbers. Point isn't the exact %, it's that growth + new products = more events to get right.

Why analytics at a derivatives exchange is harder than at a normal app

Money events must be exact

A "Deposit" or "Order Filled" event feeds finance, incentive payouts and ad ROAS. "Roughly right" - fine for a blog pageview - is not acceptable here. These belong server-side, idempotent, reconciled against the ledger.

Regulatory & geo constraints

A regulated exchange can't fire every event to every tool in every region. The CDP has to enforce consent and geo-routing at the pipeline layer - a governance problem, not a tagging one.

High event volume, real funnels

Signup → KYC → deposit → first trade → retained trader is a long, high-stakes funnel. Every drop-off is worth real money, so the schema has to model it precisely and consistently across web and app.

02

The role, decoded

The JD reads as one job but is really three distinct skill sets, and interviews will probe each. The combination is uncommon: most analytics people don't write Terraform; most engineers don't think in funnels.

Engineer

JS/TS · Node · React

Write the tracking code, client and server. Build webhook / REST integrations from the backend to third-party tools. Ship a typed instrumentation layer so events can't drift.

Analytics architect

Segment · TF

Own the event taxonomy and the "Source of Truth" schema; manage Segment / destinations as code in Terraform; audit pipelines so dashboards stay trustworthy.

Translator

Mktg ↔ Eng

Turn "which campaign drives funded accounts?" into a concrete tracking spec, and explain back to Marketing what the data can and can't say. The JD calls this out explicitly.

JD responsibilityWhat it actually means day-to-dayCovered in
Design & maintain data flow to Mixpanel/GA4/AppsFlyer via SegmentOwn the Segment Identify/Track/Page contract and the destination fan-out; keep client + server sources consistent.§06
Manage analytics infra with Terraform, not manual configSources, destinations, tracking-plan, warehouse connectors as version-controlled, peer-reviewed code.§07
Translate Marketing questions → technical tracking requirementsRun intake, write the spec, push back when a request would corrupt the schema.§05
Monitor & audit pipelines to prevent inaccurate dashboardsVolume-anomaly alerts, schema validation, a reconciliation runbook.§09
Maintain the org data-schema documentation ("Source of Truth")A versioned tracking plan in git that is the contract between eng and analytics.§05
Configure mobile + web attribution - deep-linking & postbacksAppsFlyer S2S events, deferred deep links, postbacks, SKAN, identity resolution.§08
03

Live stack teardown probed Jun 2026

I fetched bitmex.com and read the page bundle. What I could verify from outside, and what's inferred from the JD - kept clearly separate.

Segment
confirmed SEGMENT_WRITE_KEY + a separate SEGMENT_UI_WRITE_KEY in the bundle
GTM / gtag
confirmed Google Tag Manager container present → feeds GA4
Sentry
confirmed error/perf pipeline (ingest.sentry.io)
Mixpanel · GA4 · AppsFlyer
downstream named in JD; live behind Segment / in the mobile app, where a web probe can't see them
The pipeline as it stands
SOURCES
Web (React) · Mobile app · Backend (Node)
CDP
Segment
Identify · Track · Page
DESTINATIONS
Mixpanel · GA4 (via GTM) · AppsFlyer · Warehouse

Two write keys ≈ two sources (trading UI + general web). The questions that actually matter: is there a server-side (cloud-mode) source feeding money events; which destinations run device-mode (bundled SDK, ad-blockable) vs cloud-mode (sent from Segment's servers, survives blockers); and is the plan enforced by Protocols or implicit in code? The device/cloud split alone decides how much data the blockers eat.

Note

The client-side SEGMENT_WRITE_KEY is visible in the bundle (a public write key - normal). Redacted here anyway.

04

Where exchange analytics breaks

The failure modes this role exists to prevent. None throw an error - they just produce a confident, wrong number. Each maps to a fix later in the deck.

Double-counted conversions

ROAS lies

Same "Deposit" fires client- and server-side with no shared messageId → revenue looks 2×, ad budget chases a ghost. Fix: idempotency + one owner per event.

Broken identity stitching

funnel splits

Anonymous pre-signup activity never gets aliased to the userId → the funnel shows two people where there's one, acquisition source lost. Fix: deterministic identify/alias.

Schema drift

silent

Order Filled, order_filled, OrderFilled all in prod → Mixpanel reports fracture. Fix: tracking plan + Protocols validation.

Ad-blocker loss treated as truth

undercount

Privacy-heavy users drop client events; web-only signup counts read low, mobile vs web looks skewed. Fix: server-side for anything that matters.

Config drift in dashboards

no trail

Someone toggles a destination in the Segment UI; nobody knows when or why; staging ≠ prod. Fix: everything in Terraform.

Attribution black holes

spend wasted

Deferred deep link not wired → installs land on a generic screen and attribute to "organic"; paid channels look dead when they're working. Fix: AppsFlyer S2S + deep links.

Day-1 quick wins

What I'd do in the first two weeks - low-risk, high-signal, and each one builds trust before touching anything load-bearing.

Inventory every event

week 1

Pull the full list of live events firing into Segment + their destinations; flag naming inconsistencies and orphan events nobody reads. Output = the first honest schema snapshot.

Map the money funnel

week 1

Signup → KYC → deposit → first trade: confirm each step is tracked, where it fires (client vs server), and whether the numbers reconcile with finance. Find the leaks.

Stand up a tracking plan in git

week 2

Even before Protocols: a versioned spec doc as the single source of truth, so the next event added goes through review, not vibes. §05

Import existing config into Terraform state

in scope

If sources/destinations are UI-managed, terraform import them so the current reality is captured as code before any change. §07

Add a volume-anomaly alert

week 2

One simple monitor: if a top-10 event's daily volume swings ±X%, page me. Cheapest possible insurance against a silent broken dashboard. §09

Audit identity resolution

high-value

Trace one real user from anonymous → signup → funded across the pipeline; confirm alias/identify stitches history correctly. This is where the worst funnel bugs hide.

05

Event taxonomy - the "Source of Truth"

JD duty → "maintain the org data-schema documentation as the Source of Truth" + "translate Marketing questions into tracking requirements"

The deliverable the JD names directly. A tracking plan is a contract: Object-Action naming, typed properties, one owner, versioned in git, enforced by Segment Protocols so a malformed event is blocked at the edge - not discovered three weeks later in a broken Mixpanel funnel.

A slice of the plan I'd write for the core funnel

EventWhenKey propertiesSourceDestinations
Account CreatedSignup completesmethod, referral_code, utm_*serverMixpanel · GA4 · AppsFlyer · WH
KYC SubmittedDocs uploadedtier, country, attempt_noserverMixpanel · WH
KYC ApprovedVerification passestier, days_since_signupserverMixpanel · AppsFlyer · WH
Deposit CompletedFunds credited (ledger)amount_usd, asset, method, is_firstserverMixpanel · GA4 · AppsFlyer · WH
Order PlacedOrder submittedsymbol, side, order_type, leverageclientMixpanel · WH
Order FilledTrade executessymbol, contracts, notional_usd, feeserverMixpanel · AppsFlyer · WH
Funnel Step ViewedOnboarding screen seenstep_name, step_indexclientMixpanel · GA4
The rules
  • Object-Action, Title Case events; snake_case properties.
  • Money & identity events are server-sourced; UI-feel events can be client.
  • Every event has an owner, a description, and a type-checked property schema.
  • Currency always in a canonical unit (amount_usd) + raw asset alongside - never mix.
Governance

New event? PR against the plan → review → Protocols enforces it in prod and routes violations to a blocked-events stream I monitor. Adding tracking becomes a reviewable change with an owner, a description and a diff - versioned like any other code.

06

Client vs server-side & identity

JD duty → "design & maintain data flow from user interactions to Mixpanel / GA4 / AppsFlyer via Segment"

The single most important design call in the whole stack, and the one that fixes most of the §04 failure modes.

Client-side - for feel, not for money

UI interactions, screen views, funnel-step events. Fast to ship, rich context - but lossy: ad-blockers, privacy modes and dropped tabs eat a real fraction. Fine for directional product signals.

Server-side - the source of truth

Deposits, fills, KYC, account state - emitted from the backend via Segment's HTTP/Node source. Reliable, tamper-resistant, can't be blocked. For an exchange, anything that touches money or identity lives here.

Identity resolution - the signup moment, step by step
  1. Logged-out visitor browses → events attach to an anonymousId (carrying utm_* / referrer).
  2. On signup, the backend calls identify(userId, traits) - the durable identity.
  3. alias(userId, anonymousId) stitches the pre-signup history so the acquisition source survives.
  4. Money events thereafter are server-side track calls keyed to userId, with a stable messageId for idempotency.

Caveat: alias resolves differently per destination - Mixpanel merges distinct IDs, GA4 keys on its own user_id, AppsFlyer on device IDs - so identity must be reconciled in the warehouse (or Segment Unify/Profiles), never assumed consistent downstream. Get it wrong and every funnel and attribution report is quietly off.

Dedupe rule

Pick one canonical source (server for money) and give the event a deterministic messageId from a domain key (e.g. deposit_id). Segment drops identical messageIds within a ~24h window - so for late retries or cross-source dupes outside that window, you dedupe again at the warehouse on the domain key, since double-counted revenue is the most expensive failure mode here. See the code →

07

Analytics-as-code - Terraform

JD duty → "manage analytics infrastructure using Terraform, version-controlled - not manual config"

The unusual JD requirement, and the strongest signal of what they want: most analytics teams click around in dashboards; this one wants the stack in version control. The reasoning: manual config in a UI produces an analytics stack nobody can review, reproduce, or roll back.

Why IaC for analytics
  • Peer review on every tracking change - a destination toggle becomes a PR.
  • Audit trail - who changed what, when, why (critical at a regulated exchange).
  • No drift - staging mirrors prod; plan shows the diff before it ships.
  • Rollback - revert a commit instead of trying to remember the old setting.
Managed as code
  • Segment sources (web, app, server) & destinations (Mixpanel, GA4, AppsFlyer).
  • Tracking-plan / Protocols rules.
  • Warehouse connector + role grants.
  • Per-environment workspaces, remote state, CI plan on every PR.
# segment/destinations.tf - a destination as reviewable code (illustrative)
resource "segment_destination" "mixpanel_prod" {
  source       = segment_source.web_ui.id
  name         = "Mixpanel - Production"
  enabled      = true
  metadata_id  = data.segment_destination_metadata.mixpanel.id

  settings = jsonencode({
    apiSecret   = var.mixpanel_api_secret   # from secrets manager, never in git
    token       = var.mixpanel_token
    people      = true
    setAllTraitsByDefault = false       # explicit traits only - no PII leakage
  })
}

# a change to this destination is now a pull request:
#   terraform plan  →  review the diff  →  apply on merge  →  audit trail for free

Illustrative of the segmentio/segment provider, which wraps Segment's Public API - so each destination's settings schema differs and API rate limits apply on bulk applies. Two gotchas to plan for up front: Terraform state stores destination secrets in plaintext → encrypted remote backend (S3+KMS / TF Cloud) with state locking, never local state; and any resource already created in the UI must be terraform import-ed before the first apply or it gets clobbered.

08

Mobile & web attribution - AppsFlyer

JD duty → "configure mobile & web attribution pathways, including deep-linking and postbacks"

"Deep-linking and postbacks" in the JD = AppsFlyer. At a privacy-heavy crypto exchange this is the hardest part of the stack, and where deterministic tracking has mostly died - so the design has to be server-to-server first.

Deep links vs deferred deep links

A deep link opens the app to specific content when it's installed. A deferred deep link survives the install: a new user clicks a campaign, installs, and still lands on the right screen after first open - the difference between a smooth funnel and a drop-off.

Postbacks (S2S)

Server-to-server notifications back to ad networks confirming an install or in-app event, so the network can attribute and optimise. The reliable backbone when pixels are blocked.

S2S conversion events

Send Deposit Completed / Order Filled to AppsFlyer from the backend, not the device - accurate ROAS even when the client drops the event. Mirrors the server-side rule from §06.

SKAN & the privacy reality

Post-ATT iOS runs through SKAdNetwork / AdAttributionKit: a coarse/fine conversion value, Apple-randomised postback timers, and a crowd-anonymity threshold that nulls the source ID on low-volume campaigns. The real work is mapping deposit / first-trade into the limited SKAN conversion-value schema, then leaning on S2S + incrementality reads - deterministic per-user attribution is gone on iOS.

Tying it together

AppsFlyer plugs in as a Segment destination (device-mode SDK for installs, cloud-mode for server events), so its appsflyer_id has to be joined to Segment's anonymousId then userId for an install to attribute to the human who later funds an account. That join turns "1,000 installs" into "40 funded traders at $X CAC", the number Growth is judged on.

09

Data quality & the reconciliation runbook

JD duty → "monitor and audit data pipelines to prevent inaccurate dashboards"

"Monitor and audit pipelines to prevent inaccurate dashboards." The interview version of this is almost always some flavour of the reconciliation question - here's the structured answer I'd give, which doubles as the runbook I'd actually build.

"Marketing's dashboard shows 10,000 signups; finance shows 6,000 funded. They don't reconcile. Debug it."
  1. Define each metric precisely. Is "signup" a lossy client event and "funded" a reliable server ledger event? Different reliability alone can explain a gap before any bug exists.
  2. Trace one user end-to-end. Pick a real account; follow it through the Segment debugger into each destination. Find the exact stage where the event vanishes or duplicates.
  3. Check dedupe & identity. Double-fires inflating one side? Anonymous events never aliased, splitting one user into two? (The §04 + §06 failure modes.)
  4. Audit the plan. Was "signup" instrumented consistently across web and app, or are two definitions being summed?
  5. Fix & prevent. Move the critical event server-side, add Protocols validation, and add a volume-anomaly monitor so the next divergence pages a human instead of surfacing in a board deck.
Detect

Per-event volume anomaly checks (z-score / day-over-day) plus Segment Delivery Overview for silent destination drops; Protocols violation stream; dbt source-freshness on warehouse tables.

Diagnose

Segment debugger + single-user trace; a nightly dbt test reconciling money events to the ledger (row counts and summed amount_usd must match within tolerance, else alert).

Prevent

Typed events at compile time; Protocols at the edge; dbt not_null / unique / accepted_values tests in CI; every config change reviewed in Terraform.

Privacy & deletion

A GDPR / CCPA deletion request has to reach everywhere the user's events went. Segment's user deletion & suppression API propagates a right-to-be-forgotten across connected destinations and the warehouse from a single call, and consent / geo state should gate routing before events leave Segment, not after. At a regulated exchange this is required, not optional.

10

Code I'd ship

JD skill → "comfort with webhooks, REST APIs and backend-to-third-party integrations" (JS/TS · Node · React)

Talk is cheap - here are the actual artifacts behind the blueprint. The theme throughout: make the schema impossible to violate by accident, and make money events reliable.

1 · A typed tracking layer (TS) - the schema enforced by the compiler

// events.ts - the tracking plan, as types. A typo or missing prop won't compile.
type EventMap = {
  'Deposit Completed': { amount_usd: number; asset: string; method: string; is_first: boolean }
  'Order Filled':      { symbol: string; contracts: number; notional_usd: number; fee: number }
  'KYC Approved':      { tier: 'basic' | 'advanced'; days_since_signup: number }
}

export function track<K extends keyof EventMap>(event: K, props: EventMap[K]) {
  // one funnel for every event → impossible to fire an off-plan name or shape
  analytics.track({ event, properties: props })
}

track('Deposit Completed', { amount_usd: 500, asset: 'USDT', method: 'onchain', is_first: true })
// track('Deposit Completed', { amount: 500 })  ❌ won't compile - wrong shape, caught in CI

2 · Server-side money event, idempotent (Node) - can't be blocked, can't double-count

import { Analytics } from '@segment/analytics-node'
const analytics = new Analytics({ writeKey: process.env.SEGMENT_SERVER_KEY })

export async function onDepositCredited(deposit) {
  analytics.track({
    userId:    deposit.userId,
    event:     'Deposit Completed',
    properties:{ amount_usd: deposit.usdValue, asset: deposit.asset, method: deposit.method, is_first: deposit.isFirst },
    // deterministic id from the domain key → a retry collapses to ONE event, not two
    messageId: `deposit:${deposit.id}`,
    timestamp: deposit.creditedAt,
  })
}

3 · React route + funnel tracking hook - consistent Page calls, zero per-page boilerplate

function usePageTracking() {
  const { pathname } = useLocation()
  useEffect(() => {
    window.analytics?.page({ path: pathname })   // one place, every route, never forgotten
  }, [pathname])
}

Plus the integration piece the JD asks for: a small Node webhook receiver that takes events from internal services and forwards them into Segment with retries + the same idempotency rule - the "backend-to-third-party" glue, written once and reused.

Interactive prototype

A working build of this pipeline: step a trader through the funnel and watch typed events get validated, deduped, identity-stitched and fanned out to destinations in real time. Schema enforcement and idempotency run live, not just described.

11

First 90 days

Days 0-30 · Map & stabilise
  • Full event inventory + honest schema snapshot.
  • Reconcile the money funnel against finance.
  • Tracking plan v1 in git; volume-anomaly alert live.
  • Import existing Segment config into Terraform state.
Days 30-60 · Govern & harden
  • Protocols enforcing the plan; violations monitored.
  • Critical money events confirmed server-side + idempotent.
  • Typed tracking layer shipped; identity stitching fixed.
  • All destination changes flowing through Terraform PRs.
Days 60-90 · Attribute & scale
  • AppsFlyer S2S + deferred deep links audited end-to-end.
  • Install → funded-trader identity bridge verified.
  • Self-serve "request a new event" intake for Marketing.
  • A trustworthy CAC / funnel dashboard nobody second-guesses.

Sequenced deliberately: earn trust by making the existing numbers correct first, then add governance, then extend to attribution. No load-bearing change before the stack is captured as code.

12

Why me

This role lives at the seam between Marketing and Engineering - and that seam is where I've spent my career. I've grown and instrumented audiences for crypto products (deBridge, KIP Protocol), done front-line technical support at a Bitcoin L2 (Build on Bitcoin) and an Ethereum privacy app (Aztec / zk.money), and run SEO & analytics with GA / Search Console end-to-end - so I read a funnel and a tracking spec with equal comfort. I'm the translator the JD describes, with the exchange-domain context already loaded.

Strong fit

Marketing↔Eng translation · analytics & attribution thinking · GA/GSC · crypto-exchange domain · JS/TS familiarity · the data-integrity instinct this role is built on.

Proof over claims

Rather than list tools on a CV, I did the work: verified your live stack, wrote the tracking plan, the Terraform pattern and the typed event layer in this deck. That's how I ramp on any stack - by building on it.

How I work

Remote-Asia, independent, self-directed - the same way I've worked with global teams for years. I ship, measure, and tell you what the data can and can't support.

13

Method & sources

How the "confirmed" claims were verified

Fetched bitmex.com (Jun 2026) and inspected the returned page bundle for third-party integration markers. Found, verbatim in the source: SEGMENT_WRITE_KEY, SEGMENT_UI_WRITE_KEY, a Google Tag Manager container, and a Sentry ingest endpoint. Mixpanel / GA4 / AppsFlyer are not directly visible to an external web probe (they live downstream of Segment and inside the mobile app) - they're taken from the JD, and labelled as such. No private systems were accessed; everything here is from the public page and the public job description.

Role: Growth Engineer (Data Systems) - Greenhouse

Company: bitmex.com · BitMEX Blog

Stack docs: Segment Spec · Protocols · AppsFlyer S2S

IaC: Segment Terraform provider

Claims are split into confirmed (verified live from the public site) and inferred (from the JD) throughout. Code snippets are illustrative of approach, not production config. This is an unsolicited audit prepared as interview homework - happy to walk through any section live.

Prepared by Edward Tay · for the BitMEX Growth Engineer (Data Systems) role · Jun 2026