SELF-DIRECTED EXERCISE · Waypoint is fictional. Company, users, architecture and data are invented to demonstrate technical reasoning. This is not presented as client work.
← home

Architecture lab 01 · lifecycle / identity / Braze

Waypoint: design the lifecycle stack

A fictional travel company wants fast, relevant lifecycle messaging across mobile and web. The point of the exercise is not to draw the fanciest architecture. It is to show how I move from business objective to the few facts that change the recommendation, then define the trade-off, ownership and proof.

CompanyFictional travel marketplace
Scale2M registered · 650k MAU
StackMobile/Web · Braze · Snowflake · APIs
GoalReliable lifecycle activation

The decision filter.

I would use the same sequence before recommending any specific Braze feature or integration pattern.

FrameworkBusiness objective → architecture → proof
The decision filter.
Step Question
1 · Outcome What customer or business outcome are we trying to create, and what must we protect?
2 · Discovery Which few facts could actually change the answer: source, identity, data authority, latency, scale, consent, failure behavior, ownership?
3 · Recommendation What is the simplest sound path given those constraints?
4 · Trade-off What do we gain, what do we accept, and what would make me change the design?
5 · Proof How will we test the full customer path, monitor failures and know who owns recovery?
The ingestion method is a consequence of source, latency, scale and ownership, not a feature preference.

The brief.

Inspect the implementation exercise.

A local model tests 18 synthetic scenarios using the same engine in the browser and automated tests. It does not connect to Braze or send messages.

Run the exercise →

Waypoint is a mobile-first travel marketplace. Users can browse anonymously, create an account, save destinations, search flights, book trips and receive email, push and in-app messages.

The lifecycle team wants abandoned search reminders, price drop alerts, booking and pre-trip messages, post-trip review requests and personalized destination recommendations. Waypoint already has a Snowflake warehouse and a backend booking service. Braze will power orchestration and messaging.

Questions that can change the architecture

  • Where does each business fact become authoritative?
  • Which stable identifier represents the same customer across systems?
  • Which use cases need seconds or minutes, and which can tolerate scheduled freshness?
  • What volume and burst patterns should the integration survive?
  • Which system owns consent, contactability and durable profile state?
  • What happens when data is late, duplicated, rejected or contradictory?

Identity and data authority are different decisions.

“Who is this customer?” is not the same question as “which system owns this fact?” Waypoint needs one stable customer identity after authentication, but it does not need one platform to become the source of truth for every field.

Artifact 01Identity vs authority
Anonymous app user→ SDK →Anonymous Braze profile
Login / signup→ canonical customer_id →Identified profile
Account service→ current email →same profile
Booking service→ booking truth →same profile
Warehouse→ durable / derived state →same profile
DecisionWhat owns what
Identity and data authority are different decisions.
Question Choice Why
Primary person identifier Canonical customer_id after login Stable across email changes and backend systems
Current email Account / profile system Mutable contact data should not define identity
Booking status Booking service Server-authoritative transaction state
Loyalty / derived value Warehouse or loyalty source Durable or calculated state can have its own authority

Email can change without the person becoming a new customer. A stable ID answers identity; per-field authority answers which system is allowed to update each fact.

Choose ingestion by where the truth originates.

Client behaviour that naturally occurs in the app or site should usually be captured by the SDK. Trusted backend outcomes should come from the backend. Large recurring warehouse truth can use a scheduled warehouse-oriented path when immediate latency is unnecessary.

Choose ingestion by where the truth originates.
Data Source Transport Reason
destination_viewed Mobile / web client SDK Device interaction originates on the client
search_completed Client + search context SDK Behavioural moment; near-real-time
booking_completed Booking service REST Backend owns the transaction result
booking_cancelled Booking service REST Must not depend on the app opening
preferred_airport Customer profile / warehouse Scheduled sync Durable state, not a moment
lifetime_bookings Warehouse Scheduled sync Derived historical attribute

Example backend event

POST /users/track
{
  "events": [{
    "external_id": "cust_18427",
    "name": "booking_completed",
    "time": "2026-08-31T14:32:00Z",
    "properties": {
      "booking_id": "BKG_92118",
      "destination": "Paris"
    }
  }]
}

The payload carries enough context for orchestration and validation without turning Braze into a second booking database.

Real-time welcome alongside a legacy nightly batch.

Assume Waypoint has a legacy customer system that can only export an overnight CSV, but Marketing wants a welcome message within the first few minutes of signup. The wrong starting point is “how do we make the nightly file real-time?”

What I would clarify first

  • Where does signup actually occur?
  • When is the account considered authoritative?
  • Can the website, app or backend emit a real-time signal independently?
  • Which fields truly need immediate freshness versus next-day freshness?
  • If two paths touch the same field, which update wins and how is freshness protected?
Artifact 02Split latency by use case
Website / app→ signup →Backend→ REST if authoritative →Braze→Welcome journey
Legacy system→ overnight export →Batch / staging→ slower profile data →Braze

Recommendation: introduce a separate real-time path only for the fact that requires low latency. Keep slower-moving profile data on the scheduled path. Define authority and freshness for anything both paths can update.

Trade-off: two paths add governance and reconciliation work, but forcing every field into a real-time integration would add complexity without business value.

Proof: measure signup timestamp → Braze receipt → intended profile → journey entry → channel eligibility → send. Test duplicate signup, delayed confirmation and stale batch overwrite scenarios.

Abandoned search: design the exit before the message.

A user searches Toronto → Paris but does not book. Waypoint wants a reminder after two hours. The obvious build is a delay then a message. The safer build asks whether the business outcome already occurred.

Artifact 03Journey logic
search_completed→2 hour delay→Booked same itinerary?
YES→Exit
NO→Eligible + consented?→Reminder
  • Exit when booking_completed arrives for the relevant journey context
  • Define re-entry / cooldown instead of allowing accidental overlap
  • Suppress customers who are not eligible for the channel
  • Prefer the most recent valid search if multiple searches occur
  • Measure purchase conversion, drop-off and opt-out / fatigue signals
  • Use a control group when the goal is incremental lift, not just post-click conversion

Scope of the local model: each customer + search ID is evaluated separately. A production journey still needs re-entry, cooldown and “most recent search” rules across separate searches; those broader controls are design requirements here.

A million-customer launch under a request limit.

Assume a time-sensitive access message must go to 1,000,000 customers at 10:00 AM and the integration path is constrained to 10,000 requests per second. Before proposing anything, I would clarify whether that limit is per request or recipient, whether requests can batch recipients, and what “at 10:00 AM” actually means.

Artifact 04Constraint math
A million-customer launch under a request limit.
If one request = one customer 1,000,000 ÷ 10,000 requests/sec = 100 seconds minimum ingestion time, before network variance, retries or send processing.
If data can be staged Load audience and access data before 10:00 AM, then let the orchestration layer start the communication at the required business time.
If true sub-minute freshness is required Revisit batching, endpoint behavior, architecture, rate limits and whether the business SLA is achievable rather than promising an impossible clock time.

Recommendation: separate data readiness from send time wherever the business allows it. Pre-stage stable audience data, preserve only the genuinely time-sensitive trigger for 10:00 AM, and model retries, partial failure and monitoring before launch.

Trade-off: pre-staging is operationally safer but requires clear freshness rules. Real-time ingestion gives fresher data but increases rate limit, retry and recovery complexity.

Failure modes belong in the architecture.

Artifact 05Failure mode review
Failure modes belong in the architecture.
Failure Customer risk Control
Booking event delayed Reminder arrives after purchase Exit logic, timing tests, source latency monitoring
Duplicate event Duplicate downstream behavior Stable business key + dedupe / idempotency pattern
Identity mismatch Data splits across profiles Canonical ID contract + cross-source test users
Warehouse attribute stale Old tier / preference used Field-level freshness rule + fallback behavior
Consent conflict Ineligible message Channel eligibility validation before delivery
Partial high-volume failure Silent missing cohort Count reconciliation, retry policy, alerting and named recovery owner

Test the customer path, not just the component.

I would create named synthetic users covering identity, consent, timing, failure and re-entry scenarios. Then I would trace evidence from source through customer outcome.

Anonymous behavior reaches expected profile state
Login moves future activity to canonical ID
Backend event lands on the same intended profile
Mutable email updates without creating a new identity
Booking exits the matching abandonment journey
Opted-out user never receives the channel
Duplicate / replayed event does not duplicate outcome
Stale batch data cannot silently overwrite newer truth
Partial ingestion failure is detectable and recoverable
Control cohort remains unmessaged

Observability

For launch week I would reconcile source counts, ingestion counts, profile updates, journey entries, exits, sends and business outcomes. At the first divergence, the team should know which system owns the evidence and who investigates next.

A green API response, completed file transfer or successful campaign send is a checkpoint, not proof that the customer outcome worked.

Roll out in layers.

01 / INSTRUMENT

Identity + data contracts

Implement the event and profile contracts. No broad customer messaging yet. Validate schemas, ownership and profile behavior.

02 / SHADOW

Logic without broad send

Run entry, exit and eligibility logic against synthetic and internal cohorts. Reconcile expected versus actual.

03 / PILOT

Small audience

Release one use case to a controlled audience with monitoring, support ownership and a control group where useful.

04 / SCALE

Expand proven patterns

Use real adoption, failure and conversion evidence to decide when the architecture and operating model are ready for more journeys or higher volume.

What changes at 10M users?

The conceptual architecture may stay recognizable, but the operating model has to mature:

  • Formal event schema and data owner governance
  • Automated contract tests between source services and ingestion
  • Capacity and rate limit modeling for bursts
  • More explicit privacy, retention and regional consent controls
  • Monitoring for ingestion lag, stale data and count divergence
  • Reusable lifecycle components instead of one-off journeys
  • Change management rules for data consumed by multiple downstream use cases
  • Operational runbooks, escalation paths and clear recovery ownership
At scale, technical maturity is not more architecture. It is clearer ownership, safer change, better observability and fewer surprises.