Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Senior Full-Stack Live Coding: Async Job Control

This is a 90-minute senior full-stack exercise. You are joining a small working system that simulates asynchronous video-processing jobs. Creating, listing, polling, inspecting, completing, and deterministically failing jobs already work.

Your task is to deliver one production-minded vertical slice: Cancel + Retry. The interesting part is not the two buttons. It is keeping the API, background runner, concurrent requests, and optimistic UI consistent when operations race.

Stack and prerequisites

  • API: Python 3.11+, FastAPI, Pydantic, pytest
  • Web: Node 20+, React, TypeScript, Vite, Vitest
  • No database, queue, Docker, or external services are required

Install everything:

make setup

Confirm the starter is healthy:

make check

The supplied acceptance tests describe the unfinished behavior and are intentionally red at the start:

make acceptance

Before implementation this command should report 9 failing backend acceptance tests and 4 failing frontend acceptance tests. Do not delete, skip, weaken, or rewrite those tests.

Run the app in two terminals:

make run-api
make run-web

Open http://localhost:5173. API docs are at http://localhost:8000/docs.

What to implement

Add a CANCELLED job status and these endpoints:

POST /jobs/{id}/cancel
POST /jobs/{id}/retry

The state transitions are:

QUEUED ──────┐
             ├── cancel ──> CANCELLED ──┐
PROCESSING ──┘                          │
                                        ├── retry ──> QUEUED
FAILED ─────────────────────────────────┘

Cancel contract

  • QUEUED or PROCESSING → CANCELLED.
  • Preserve the progress reached when cancellation wins.
  • Clear outputUrl and append a timeline event.
  • Cancelling an already CANCELLED job is idempotent: return 200 without appending another cancellation event.
  • Cancelling DONE or FAILED returns 409 Conflict.

Retry contract

  • FAILED or CANCELLED → QUEUED.
  • Increment attempt, reset progress to 0, clear error and outputUrl, and append a timeline event.
  • Start exactly one new background attempt.
  • Retrying QUEUED, PROCESSING, or DONE returns 409 Conflict.

Both endpoints return the updated Job on success. Missing jobs return 404 with {"detail": "Job not found"}. Invalid transitions return 409 with a stable, useful detail string.

Concurrency invariants

Treat these as correctness requirements, not optional hardening:

  • Two concurrent retry requests for the same retryable job result in exactly one 200 and one 409.
  • An older attempt must never update a newer attempt after retry.
  • Once cancellation wins, the cancelled attempt must never mutate progress, status, error, or output afterward.

Keep transitions atomic in the store. The background simulator already carries an attempt token; preserve and complete that fencing model rather than replacing it with timing assumptions.

Web behavior

Complete the API client and expose actions in the existing job details drawer:

  • Show Cancel job only for QUEUED and PROCESSING.
  • Show Retry job only for FAILED and CANCELLED.
  • Track pending state per job/action so rapid duplicate clicks issue only one request and the in-flight action is visibly disabled.
  • Reconcile the returned job into both the list and open drawer without a full-list refresh.
  • Surface an action error and keep the last valid job state intact.

Do not remove the existing optimistic creation or smart polling behavior.

Tests and completion

The acceptance suites are executable requirements:

make backend-acceptance
make frontend-acceptance

Add at least:

  • one meaningful backend test beyond the supplied acceptance tests; and
  • one meaningful frontend test beyond the supplied acceptance tests.

Avoid tests that merely assert a mock was called. Prefer a state-machine edge, race, error recovery, or observable user behavior.

The task is complete when:

make verify

passes without modifying the published acceptance criteria.

Suggested 90-minute shape

  • 0–10 min: read the model, store, runner, UI, and tests; state your plan.
  • 10–70 min: implement the smallest correct vertical slice.
  • 70–85 min: add tests and close race/error gaps.
  • 85–90 min: run verification and explain production trade-offs.

We value a coherent, verified slice over a large unfinished rewrite. Explain assumptions and trade-offs as you work.

Repository map

api/app/models.py       Job contract and validation
api/app/store.py        In-memory atomic mutation boundary
api/app/runner.py       Deterministic asynchronous simulator
api/app/main.py         FastAPI routes
api/tests/acceptance/   Published backend contract

web/src/api.ts          HTTP client
web/src/JobsPage.tsx    State, reconciliation, and actions
web/src/components/     Form, list, and details drawer
web/src/hooks/          Visibility-aware polling
web/src/acceptance/     Published frontend contract

Evaluation rubric

The rubric is public; there are no hidden product requirements.

Area Points What strong evidence looks like
API state machine 30 Exact transitions, response codes, resets, events, and idempotency
Race safety 25 Atomic compare-and-set behavior, one retry winner, stale-attempt fencing
Frontend integration 20 Correct visibility, duplicate prevention, response reconciliation, errors
Tests and verification 15 Existing suites preserved, useful new tests, clean lint/build
Senior reasoning 10 Clear plan, focused changes, explicit trade-offs and production path

Intentionally out of scope

Do not spend the live session adding a database, authentication, a real task queue, SSE/WebSockets, media processing, deployment, or a visual redesign.

If this were production, we would discuss durable compare-and-set updates, idempotency keys, distributed worker cancellation, an outbox/event log, authorization, observability, and push updates. Those are valuable discussion topics; they are not required implementation here.

CI runs the healthy baseline suites, lint, and build. The separate acceptance suites remain red until the task is implemented so the starter repository itself can stay green and reproducible.

About

90-minute senior full-stack live coding task

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages