This guide uses the current Software Engineer — Affiliate Operations opening as its main track. Its public description connects engineering work to partner integrations, customer acquisition and operational reliability. That makes a useful preparation question very concrete: when a retailer sends late or repeated data, how do you protect the result a shopper or business team sees?
Build one small practice service around offers and campaign events. Start with a contract for price units, stock status and identifiers, then add a repeatable data import and a daily report. Keep a failed import out of the visible catalog and make a repeated conversion event harmless. These exercises create a coherent story across coding, SQL, system design and debugging rather than four unrelated answers.
Use Python and relational data as the main implementation path for this opening. Lyst lists Django, AWS, PostgreSQL and messaging services in the role description, alongside testing and service ownership. A small local fixture is enough to practise the reasoning, but be precise about its limits: a passing SQLite transaction test does not establish the throughput of a cloud pipeline. Bring the contract, a failure case and the measurement you would run next.
Connect your experience to the product
editorialBegin with a specific part of the affiliate platform: a partner integration, a product-data service or a campaign decision. Explain the user or business outcome it serves before naming a tool. This gives your technical examples a clear purpose and helps distinguish a reliability requirement from an optional improvement.
What to demonstrate
- Explain a data contract in terms another team can check: what one record represents, which identifier is stable and what happens when a partner repeats a delivery.
- Connect an engineering decision to a shopper or partner outcome, such as preventing an unavailable offer from being displayed or keeping a report consistent.
How to prepare
- Prepare a short diagram from partner input to stored data and the consumer of that data. Mark the point where an incomplete import becomes visible and how you would prevent that.
- Choose one past integration or data-quality problem. State your own contribution, one important constraint and the evidence that showed the final behavior was better. Keep estimates separate from measurements you actually recorded.
Practise precise backend changes
editorialWork through a contained Python change with an explicit input contract. An offer-selection function is a good rehearsal because small ambiguities change the answer: currency, unavailable inventory and equal prices all matter. Make those choices visible in tests before discussing a larger application structure.
What to demonstrate
- Keep the result deterministic across different input orders, and explain why comparing amounts without a shared currency or unit would be incorrect.
- Select a representation whose cost you can describe, including the number of products held in memory and the effect of a very large partner feed.
How to prepare
- Implement the cheapest-offer exercise, then add empty input, equal prices, unavailable stock and malformed price cases. Run the same input in a different order and compare the result.
- Wrap one operation in a simple API contract on paper: validation errors, a successful response and a repeated request. Explain where application validation ends and a database constraint must protect the stored state.
Reason across data and system boundaries
editorialPractise a query and a failure scenario together. A campaign report can look plausible even when joining two event tables multiplies its totals. A product feed can look complete even when a stale retry replaces a newer snapshot. Use a small counterexample to make each defect observable.
What to demonstrate
- Distinguish events, entities and aggregate rows before joining data; show how row multiplicity affects a count or sum.
- Identify the single publication boundary in a feed pipeline and explain which state must remain consistent if a download, validation or delivery attempt fails.
How to prepare
- Create three campaigns with different click and order counts, including a campaign with no activity. Predict the report by hand, then compare it with both a direct join and independent aggregates.
- Walk through a feed arriving out of order. Keep staging separate from publication and state the version rule that stops an older successful download from becoming the current catalog. Describe a safe recovery step using the previous complete snapshot.
Explain decisions and the work after release
editorialPrepare a service-ownership example that includes a result after the first release. Lyst’s current opening describes engineers operating the systems they build. Its 2021 engineering-culture article also provides dated context on constructive feedback and sustainable delivery; use your own evidence to explain how you work.
What to demonstrate
- Make the operational consequence of a choice clear: which signal detects a problem, who can act on it and what a safe response looks like.
- Explain a disagreement with Product, Marketing, Analytics or another partner by showing the competing objectives and the evidence used to decide.
How to prepare
- Bring one example where a metric challenged your first interpretation. Describe the population, time window and an alternative explanation you checked before recommending a change.
- Write a brief handoff for the practice feed service: the normal freshness check, the failed-import signal and the rollback condition. Ask another person to follow it without a verbal explanation, then improve the part they could not reproduce.
PracHub editorial advice for the preparation topics above.
Comparing offers before defining money and availability
State the currency, price unit and stock rule first. An integer amount of 1299 has no useful ordering against another currency unless a conversion policy exists. An unavailable offer should not win simply because it is cheaper. In your practice function, cover equal prices and changing input order so a reviewer can see that the result follows a contract rather than whichever record happened to arrive first.
Accepting a plausible campaign total without checking row multiplicity
Before optimizing a report, draw the grain of each table and predict one campaign’s result by hand. Three clicks joined to two orders can produce six rows; summing on that result inflates revenue even though the SQL executes successfully. Aggregate independent event streams separately, then join their summaries. Include a campaign with no events so missing activity is not confused with a missing campaign.
Treating the last feed to finish as the newest catalog
A slow retry can finish after a newer export. Use an explicit version contract and separate validation from publication, keeping the current catalog intact until a complete candidate is ready. Walk through both a malformed feed and an older successful feed. Your recovery explanation should identify the previous complete snapshot and the freshness signal that tells an operator the system is behind.
Calling an experiment successful from one improved number
Explain the assignment unit, time window and guardrail before interpreting the result. An apparent campaign improvement might coincide with inventory changes or a different partner mix. State a competing explanation and the evidence that would distinguish it. If the data cannot answer the original question, make the narrower decision it supports instead of presenting confidence that the experiment did not earn.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Choose the cheapest available offer per product
Given unique offer IDs, product IDs, integer prices in minor units, currency and stock status, select one available offer per product in a requested currency. Break equal-price ties by offer ID; omit products without an eligible offer.
Approach
- Keep only the best (price, offer ID) pair per product. Do not compare prices across currencies.
- Validate price units before filtering, and make the tie-break independent of input order.
Worked solution 35 min
Select a deterministic cheapest offer
Implement the one-currency offer-selection contract above. Input offer IDs are unique; prices are non-negative integer minor units.
- Track the best price/ID tuple per product.
- Skip unavailable offers and currencies outside the request.
- Test ties and stock state before discussing database indexing.
def cheapest_available(offers, currency="GBP"):
best = {}
for offer in offers:
price = offer["price_minor"]
if type(price) is not int or price < 0:
raise ValueError("price must be non-negative integer minor units")
if not offer["in_stock"] or offer["currency"] != currency:
continue
product = offer["product_id"]
candidate = (price, offer["offer_id"])
if product not in best or candidate < best[product]:
best[product] = candidate
return {product: offer_id
for product, (_, offer_id) in sorted(best.items())}
Scroll sideways to view long lines.
Follow-up
- How would you support a retailer-specific exclusion without reprocessing the full catalog?
Detect a price crossing without duplicate alerts
Process versioned prices for one product. Emit an alert when the price moves from above a subscriber’s threshold to at or below it. Ignore older versions and exact replays; a later rise above the threshold makes a new crossing eligible.
Approach
- Track the highest accepted version and the previous side of the threshold.
- Distinguish an initial observed low price from a crossing; choose that contract before coding.
Follow-up
- Where would you place a durable idempotency key for the notification?
Find missing pages in a partner export
An export declares page IDs 1 through N. Page deliveries can repeat and arrive out of order. Return missing IDs in ascending order and reject IDs outside the declared range.
Approach
- Use a set or bitmap for received pages and calculate the complement only after the export closes.
- Treat repeated pages as deliveries of the same page; different content at one page ID needs a separate conflict policy.
Follow-up
- How would you change the representation when N is too large for memory?
Report daily campaign activity without join inflation
Given campaigns, click events and completed orders, report clicks, orders and revenue per campaign for one UTC day. Event IDs are unique and all revenue uses one currency. Include campaigns with no activity.
Approach
- Aggregate clicks and orders independently before joining the campaign table.
- Use a half-open day interval and preserve zero-activity campaigns with left joins.
Worked solution 35 min
Aggregate before joining campaign events
Use campaigns(id), clicks(id, campaign_id, happened_at) and orders(id, campaign_id, happened_at, status, revenue_minor). Every event ID is unique; one currency is used. Run the complete SQL block in an empty SQLite database.
- Filter each event stream to the same half-open UTC interval.
- Aggregate each stream by campaign before joining.
- Use the campaign table as the base so zero-activity rows survive.
CREATE TABLE campaigns(id TEXT PRIMARY KEY);
CREATE TABLE clicks(id TEXT PRIMARY KEY, campaign_id TEXT, happened_at TEXT);
CREATE TABLE orders(id TEXT PRIMARY KEY, campaign_id TEXT, happened_at TEXT,
status TEXT, revenue_minor INTEGER);
INSERT INTO campaigns VALUES ('a'),('b'),('c');
INSERT INTO clicks VALUES ('a1','a','2026-09-01T01:00:00Z'),
('a2','a','2026-09-01T02:00:00Z'),('a3','a','2026-09-01T03:00:00Z'),
('b1','b','2026-09-01T04:00:00Z'),('next','a','2026-09-02T00:00:00Z');
INSERT INTO orders VALUES ('o1','a','2026-09-01T02:00:00Z','completed',2000),
('o2','a','2026-09-01T03:00:00Z','completed',3000),
('cancel','a','2026-09-01T04:00:00Z','cancelled',9000),
('boundary','a','2026-09-02T00:00:00Z','completed',7000);
-- Solution query
WITH click_totals AS (
SELECT campaign_id, COUNT(*) AS clicks
FROM clicks
WHERE happened_at >= '2026-09-01T00:00:00Z'
AND happened_at < '2026-09-02T00:00:00Z'
GROUP BY campaign_id
), order_totals AS (
SELECT campaign_id, COUNT(*) AS orders,
SUM(revenue_minor) AS revenue_minor
FROM orders
WHERE status = 'completed'
AND happened_at >= '2026-09-01T00:00:00Z'
AND happened_at < '2026-09-02T00:00:00Z'
GROUP BY campaign_id
)
SELECT c.id,
COALESCE(ct.clicks, 0) AS clicks,
COALESCE(ot.orders, 0) AS orders,
COALESCE(ot.revenue_minor, 0) AS revenue_minor
FROM campaigns AS c
LEFT JOIN click_totals AS ct ON ct.campaign_id = c.id
LEFT JOIN order_totals AS ot ON ot.campaign_id = c.id
ORDER BY c.id;
Scroll sideways to view long lines.
Follow-up
- Why are daily orders divided by daily clicks not necessarily a cohort conversion rate?
Find current in-stock offers from versioned updates
Given offer_updates(retailer_id, offer_id, version, in_stock, price_minor), return the latest available offers. Versions are unique per retailer and offer. Include a case where the latest update removes stock.
Approach
- Rank by descending version within the retailer/offer pair.
- Filter for in-stock rows after selecting the latest version, so an older available row cannot reappear.
Follow-up
- What contract should handle two different payloads at the same version?
Publish a partner feed without exposing partial data
Design a pipeline that downloads a partner product feed, validates it and publishes a complete catalog snapshot. Cover partial downloads, stale retries, invalid rows and rollback.
Keep a late feed worker out of the live catalog
Choose a scenario to trace what changes.
The current worker finishes a complete newer partner feed.
- 01Stage a candidateKeep downloaded rows invisible while checking completeness and the feed contract.
- 02Validate ownership and versionRequire the current worker token and a version newer than the visible snapshot.
- 03Move the visible pointerAtomically publish one complete immutable snapshot.
Publish the validated snapshot only if both worker ownership and feed version are eligible.
PracHub partner-feed model. The ownership token and partner snapshot version solve different problems: who may publish, and which data is newer. Check both atomically. This extends the simplified version-only code example.
Approach
- Stage and validate an immutable candidate before updating the reader-visible version pointer.
- Use a monotonic version contract and an atomic publication step; retain the previous valid snapshot for recovery.
Worked solution 45 min
Model the feed publication boundary
Separate immutable candidate data from the visible version pointer. Use this small model to check which candidate may become current.
- Download into a separate candidate and validate its declared schema, row count and completeness policy.
- Assign monotonic versions under a documented partner contract; arrival time is not the version.
- Publish only validated newer candidates through an atomic compare-and-swap or equivalent transaction.
- Keep the previous complete snapshot and monitor freshness plus rejected-row signals.
def publish_snapshot(current_version, candidate_version, valid):
if not valid:
return current_version, "rejected-invalid"
if current_version is not None and candidate_version <= current_version:
return current_version, "ignored-stale"
return candidate_version, "published"
Scroll sideways to view long lines.
Follow-up
- When should one invalid row reject the whole feed rather than enter a quarantine?
Ship a campaign experiment with a reliable readout
Design an experiment for a new product-selection rule in an affiliate campaign. Define assignment, event recording, guardrails and a decision that can be made from the resulting data.
Approach
- Keep assignment stable at a declared unit and version the experiment configuration.
- Track both the primary result and a failure guardrail; explain late events, exclusions and what you would do when metrics disagree.
Follow-up
- How could a change in retailer inventory contaminate a before-and-after comparison?
Stop a replayed conversion from inflating revenue
A partner retries a conversion event after a timeout. The handler increments revenue again although the source and event ID match an earlier delivery. Reproduce the failure and make ingestion idempotent.
Approach
- Enforce uniqueness on (source, event ID), then update the aggregate only when a new event is inserted.
- Keep event insertion and aggregate change in one transaction; reject reuse of an event ID with different values.
Worked solution 40 min
Commit each conversion’s effect once
The SQLite fixture gives conversions a primary key of (source, event_id) and revenue_totals a primary key of campaign_id. Keep ingestion and the total in one transaction. The complete Python block creates an in-memory database and verifies a repeated event.
- Reproduce a repeated delivery incrementing the old handler twice.
- Insert the uniquely identified event and change the total only when insertion succeeds.
- On replay, compare the existing values and reject conflicting reuse.
- Test rollback when updating the aggregate fails.
def record_conversion(db, source, event_id, campaign_id, revenue_minor):
with db:
inserted = db.execute(
"INSERT OR IGNORE INTO conversions VALUES (?, ?, ?, ?)",
(source, event_id, campaign_id, revenue_minor),
).rowcount
if not inserted:
existing = db.execute(
"SELECT campaign_id, revenue_minor FROM conversions "
"WHERE source = ? AND event_id = ?",
(source, event_id),
).fetchone()
if existing != (campaign_id, revenue_minor):
raise ValueError("event ID reused with different values")
return False
db.execute(
"INSERT INTO revenue_totals VALUES (?, ?) "
"ON CONFLICT(campaign_id) DO UPDATE SET "
"revenue_minor = revenue_totals.revenue_minor + excluded.revenue_minor",
(campaign_id, revenue_minor),
)
return True
import sqlite3
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE conversions(source TEXT, event_id TEXT, campaign_id TEXT,
revenue_minor INTEGER CHECK(revenue_minor >= 0), PRIMARY KEY(source,event_id));
CREATE TABLE revenue_totals(campaign_id TEXT PRIMARY KEY, revenue_minor INTEGER);
""")
assert record_conversion(db, "partner-a", "event-1", "campaign-a", 2500)
assert not record_conversion(db, "partner-a", "event-1", "campaign-a", 2500)
assert db.execute("SELECT revenue_minor FROM revenue_totals").fetchall() == [(2500,)]
db.close()
Scroll sideways to view long lines.
Follow-up
- How would you process a legitimate later correction without silently replacing the original event?
A PracHub practice schedule: complete one pair of related tasks per session and keep the result you can explain or run. Adjust the pace to your experience; this is not an employer hiring timeline.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Map the partner-data role
- Read the opening and sketch input, storage and consumer boundaries.
- Specify price units, availability and deterministic tie-breaking.
Deliverable: A one-page role map; Five boundary cases
Practice prompt ↗02Implement offer selection
- Run the coding solution and add one adversarial input.
- Solve threshold crossings and distinguish replay from a new event.
Deliverable: Tested Python selection; A state-transition table
Practice prompt ↗Worked solution ↗03Complete a partner export
- Identify missing pages and explain the close-of-export signal.
- Predict fixture totals by hand before running the query.
Deliverable: A completeness contract; A correct report and a bad-join counterexample
Practice prompt ↗Worked solution ↗04Review versioned stock
- Test a latest update that removes availability.
- Separate candidate validation from changing the visible pointer.
Deliverable: A query with an anti-regression case; A publication diagram
Practice prompt ↗Worked solution ↗05Reproduce a duplicate
- Run replay and rollback tests for conversion ingestion.
- Define assignment, metric, guardrail and a competing explanation.
Deliverable: An idempotent transaction; An experiment decision note
Practice prompt ↗Worked solution ↗06Explain measured impact
- Rehearse a real decision where evidence changed your mind.
- Explain a data-quality tradeoff to a non-engineering listener.
Deliverable: A concise measurement story; An understandable acceptance check
Practice prompt ↗Practice prompt ↗07Run an ownership walkthrough
- Demonstrate a normal run, failed input and recovery.
- Repeat the exercise with the least convincing evidence.
Deliverable: A reproducible service handoff; A revised solution and final review notes
Practice prompt ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Use your own examples of measurement, stakeholder decisions and operating a service after release.
Explain a change whose value you measured
Describe a feature where an initial result looked positive but a deeper measurement changed your conclusion.
Approach
- State the decision, the metric and the population being measured.
- Explain the conflicting evidence and the action you took rather than presenting only the favorable number.
Follow-up
- What observation would have made the original conclusion credible?
Resolve a technical disagreement with a business partner
Describe a disagreement about data quality, delivery timing or feature scope with someone outside engineering.
Approach
- Explain the other person’s goal and the constraint your system had to respect.
- Show the smaller agreement, acceptance check and ownership of follow-up.
Follow-up
- How did you make the outcome understandable without asking them to read your code?
Own a service after the feature ships
Describe a service you improved after observing it in production. Include an alert, an operational task and a lasting correction.
Approach
- Connect an actual symptom to a reproducible cause.
- Show how the fix reduced repeated work and how another engineer could operate the service afterward.
Follow-up
- What did you intentionally leave manual and why?
- 01
Choose one business-facing decision and one production failure you can explain with concrete evidence.
Which Lyst engineering role does this guide focus on?
The main track is the public Software Engineer — Affiliate Operations opening, covering partner integrations, product data and acquisition systems. Lyst’s careers page lists other engineering roles with different responsibilities.
Lyst — Software Engineer, Affiliate Operations ↗Lyst — Careers ↗Should I prepare Python or frontend engineering?
For this opening, start with Python, Django and relational-data reasoning. The role also lists cloud, messaging and delivery tools; React and TypeScript are additional advantages rather than the main preparation track used here.
Lyst — Software Engineer, Affiliate Operations ↗Does Lyst publish a fixed interview loop for this role?
The reviewed opening does not specify interview rounds, their length or a technical-task format. The cards above organize preparation around the role’s responsibilities; they do not describe four confirmed interviews.
Lyst — Software Engineer, Affiliate Operations ↗Do these exercises reproduce Lyst interview questions?
They are original PracHub practice exercises built around partner data, measurement and reliability. Use the runnable fixtures to check your reasoning, then practise explaining the same decisions in your own language and stack.
Sources & methodology 5 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01Lyst — Software Engineer, Affiliate Operations ↗
Role responsibilities and stack, verified in the live posting. No interview sequence is stated.
official · Accessed 2026-09-20 - 02Lyst — Careers ↗
Current engineering openings and company context.
official · Accessed 2026-09-13 - 03Lyst — About us ↗
Fashion shopping and discovery product context.
official · Accessed 2026-09-13 - 04Lyst Engineering — Our Engineering Culture (2021) ↗
Historical engineering principles; used as dated context, not a current hiring policy.
official · Accessed 2026-09-20 - 05PracHub — Software Engineer questions ↗
Software Engineer practice across companies.
platform · Accessed 2026-09-12