FullStack Course LogoFullStack Course
Module: Machine Coding
Machine Coding·246·9 MIN READ

246: Full-Stack Machine-Coding Challenge: CRUD, Auth Context, and Persistence

TOPICS COVERED: Full-Stack Machine-Coding Challenge: CRUD, Auth Context, and Persistence

Learning outcomes

By the end of this lesson, you can:

  • explain and apply an API contract in a realistic implementation;
  • explain and apply persistence in a realistic implementation;
  • explain and apply auth context in a realistic implementation;
  • explain and apply optimistic concurrency in a realistic implementation;
  • explain and apply end-to-end typing in a realistic implementation.

Prerequisites and retrieval

This lesson assumes the earlier 01–06 foundation and the preceding lessons in this module. Before you start, retrieve one concrete example from an earlier project in which one of these concerns appeared. The point is not to recite terminology. In a timed machine-coding exercise, you need to make a defensible decision and keep the result readable, accessible, testable, and straightforward to extend when interview pressure is high.

Terminology

  • API contract: Define routes, request validation, statuses, and response shapes before connecting the UI to them.
  • Persistence: Enforce uniqueness and references with database constraints, and use parameterized queries for data access.
  • Auth context: When authentication is out of scope, represent the current user with a documented stubbed principal.
  • Optimistic concurrency: For editable data, define what happens when two clients try to update from stale versions of the same record.
  • End-to-end typing: With TypeScript, share stable, schema-derived contracts carefully, while retaining runtime validation at the server boundary.
  • Run instructions: A reviewer should be able to install, configure, migrate or seed, run, and test the project from a short README without relying on undocumented local state.

Mental model

Treat Full-Stack Machine-Coding Challenge: CRUD, Auth Context, and Persistence as a design problem with observable inputs, outputs, invariants, and failure modes. A full-stack round evaluates API and persistence correctness in addition to UI execution. The most reliable strategy is still a thin vertical slice with stable contracts, not a broad architecture that remains unfinished. A strong implementation makes assumptions visible, limits uncertainty at boundaries, and leaves evidence—tests, types, constraints, metrics, or diagrams—that explains why the design is safe.

A useful sequence for both an interview and production work is:

text
requirement -> constraints -> model -> implementation -> failure analysis -> verification

Do not move straight from a requirement to a library call. First state what must remain true. Then select the mechanism that enforces that property. This keeps the implementation explainable when an edge case or failure path appears.

Deep dive

1. API contract

Define routes, request validation, status codes, and response shapes before wiring the UI. Use one error format so client code can handle failures consistently instead of branching on ad-hoc message strings.

Decision rule: Use an API contract deliberately when it makes the contract or invariant easier to prove. If an approach only reduces typing while hiding an assumption, prefer the more explicit design.

2. Persistence

Use database constraints to enforce uniqueness and references, and use parameterized queries for all data access. Make migrations and seed data deterministic enough that a reviewer can run the project from a clean checkout and reach the same starting point.

Decision rule: Use persistence deliberately when it makes the contract or invariant easier to prove. If it only reduces typing while hiding an assumption, prefer the more explicit design.

3. Auth context

When authentication is out of scope, use a documented stubbed principal so the rest of the application can still receive user or tenant context explicitly. When authentication is included, keep the distinction clear: hiding a button in the client is not the same as enforcing authorization on the server.

Decision rule: Use auth context deliberately when it makes the contract or invariant easier to prove. If it only reduces typing while hiding an assumption, prefer the more explicit design.

4. Optimistic concurrency

For editable data, decide how two clients with stale data should behave. A version field or update timestamp is often enough to demonstrate conflict detection without building a full real-time collaboration system. The key is that the update must check the version it was based on, rather than silently overwriting a newer change.

Decision rule: Use optimistic concurrency deliberately when it makes the contract or invariant easier to prove. If it only reduces typing while hiding an assumption, prefer the more explicit design.

5. End-to-end typing

If the project uses TypeScript, share stable schema-derived contracts where that improves consistency, but keep runtime validation at the server boundary. Types disappear at runtime, so they cannot validate untrusted requests by themselves. Also avoid importing server-only framework types into browser bundles; a shared contract should remain safe for the environments that consume it.

Decision rule: Use end-to-end typing deliberately when it makes the contract or invariant easier to prove. If it only reduces typing while hiding an assumption, prefer the more explicit design.

6. Run instructions

A reviewer should be able to install dependencies, configure the application, migrate or seed the database, run the project, and execute its tests using a short README. Do not make undocumented local state part of the setup; if a prerequisite is required, name it and show how to establish it.

Decision rule: Use run instructions deliberately when it makes the contract or invariant easier to prove. If it only reduces typing while hiding an assumption, prefer the more explicit design.

Worked example

Consider a timed machine-coding exercise that must stay readable, accessible, testable, and easy to extend under interview pressure. Start by stating the requirement in one sentence. Then list the input and output contracts and assign each failure mode to the concept that owns it. The useful separation is this: parsing and validation happen at the boundary; domain rules belong in the domain or service layer; persistence rules belong in the database or repository; and presentation rules belong in the client. Mixing those concerns can make a happy-path demo look shorter, but it makes edge cases much harder to reason about.

ts
export async function handleRequest(input: unknown) {
  const command = parseCommand(input);
  const result = await service.execute(command);
  return toHttpResponse(result);
}

Walk through at least four cases: the normal path, an empty or missing value, a duplicate, retry, or concurrent path where relevant, and a dependency failure. For each case, identify the layer that detects the problem and describe what the caller observes. That exercise exposes whether the boundaries are real or merely names in the folder structure, and it is the level of explanation expected in a senior code review or technical interview.

Production perspective

Production correctness is broader than “the code works on my machine.” Consider how the design behaves during deploys, retries, partial failures, stale-client updates, concurrent requests, malformed data, schema changes, and high-cardinality workloads. Prefer explicit contracts, bounded resource usage, structured errors, and measurable behavior. Optimize only after you can identify the bottleneck or risk with evidence.

When an external dependency is involved, define a timeout and cancellation strategy. When persistence is involved, define the transaction and consistency expectations. When the feature has user-visible state, define loading, empty, error, stale, and success states. When security is involved, assume that the client can be modified and that network input is untrusted.

Guided lab

Run a 3-hour full-stack issue tracker with list, create, edit, and status-transition flows; tenant and user context; PostgreSQL persistence; validation; one concurrency check; a React + TanStack Query v5 client; tests; and exact run instructions.

Complete the lab with this discipline:

  1. Write the requirement and two non-requirements.
  2. List the input, output, and error contracts before implementation.
  3. Implement the smallest correct vertical slice.
  4. Add at least one invalid-input test and one edge-case test.
  5. Instrument or inspect the behavior instead of guessing.
  6. Refactor one hidden assumption into an explicit type, constraint, function, or configuration value.
  7. Explain one alternative design and why you did not choose it.
  8. Record a short “what would break at 10× scale?” note.

Edge cases and failure modes

  • API contract: Test missing data, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Persistence: Test missing data, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Auth context: Test missing data, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Optimistic concurrency: Test missing data, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • End-to-end typing: Test missing data, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.

Common mistakes and debugging

  • Solving the example instead of the requirement: a copied pattern can be syntactically correct while still being architecturally wrong.
  • Hiding uncertainty with assertions, broad exception handlers, permissive schemas, or “temporary” any values.
  • Testing only the happy path, then discovering the actual contracts during integration.
  • Optimizing before measuring, or selecting a scalable mechanism without a scale requirement.
  • Treating client-side behavior as a substitute for server-side authorization, validation, or persistence guarantees.

For debugging, reproduce the smallest failing case first. Inspect the actual value or execution plan, trace the boundary at which the invariant first becomes false, and repair the layer that owns the rule instead of adding a downstream patch. The useful boundaries to distinguish are the source or build, browser or DOM, Network or HTTP, server or route, database or query, and deployment or configuration.

Interview questions

  1. What problem does an API contract solve, and what trade-off or failure mode would make you choose a different approach?
  2. What problem does persistence solve, and what trade-off or failure mode would make you choose a different approach?
  3. What problem does auth context solve, and what trade-off or failure mode would make you choose a different approach?
  4. What problem does optimistic concurrency solve, and what trade-off or failure mode would make you choose a different approach?
  5. What problem does end-to-end typing solve, and what trade-off or failure mode would make you choose a different approach?

Checkpoint

Without notes, explain Full-Stack Machine-Coding Challenge: CRUD, Auth Context, and Persistence to another developer in five minutes. Include one invariant, one edge case, one production failure mode, and one alternative design. Then implement a small example without copying the lesson code.

Mastery checklist

  • I can define the core terms precisely.
  • I can choose a design from requirements instead of from habit.
  • I can implement and test the normal path and edge cases.
  • I can explain the runtime, storage, or complexity cost.
  • I can identify which layer owns validation, errors, and recovery.
  • I can compare at least two reasonable alternatives.
  • I can explain how the design changes at larger scale or under stricter reliability requirements.

References

Reader page: /machine-coding/lesson/246/full-stack-machine-coding-challenge-crud-auth-context-and-persistence