Projects / A point of viewPreview

Build something.
Go deeper.

A familiar idea can make a strong portfolio. Give it real constraints, make it fail, and show how you brought it back.

The interesting part starts after the happy path: a worker dies, a request arrives twice, one user tries to read another user’s data. What did you decide? What evidence says the decision holds?

The premise

Depth is a set of decisions you can defend.

A clone, a shop, or a dashboard can contain serious engineering. A novel idea can still stop at a polished interface. Choose a small product you care about, then follow its responsibilities all the way through.

Use AI to help build it if you like. Keep ownership of the reasoning: explain the code, challenge its assumptions, reproduce its failures, and describe what you would change.

Beginner projects / A smaller starting point

Practice the work.

One responsibility. A clear finish line. Room to go deeper.

Choose the work you want to get better at. Follow either path independently, finish its first exercise, and investigate one failure before adding more. Each next step extends something you already understand. Open an exercise for its scope, finish line, and failure check.

Backend 01 → 02

Own a business rule.

Start with a small module you can test. Let storage, audit, and useful boundaries grow from a working behavior.

Before you startFunctions, basic data types, and running a test in your language. Go is a good fit; the exercise does not depend on it.

  1. Start here Organization membership Identity, permissions, and explicit errors
    Build this much
    Seed one organization, a fixed owner, and a few existing users. Write functions to add a member, change a member between viewer and editor, and list members. Only the owner can make changes. Begin with in-memory storage and pass known actors from a test harness.
    Your finish line
    Tests show an owner adding and updating a member, a viewer being denied, and a duplicate membership being rejected. Each rejection leaves the original state intact and returns an error the caller can distinguish.
    Make one thing fail
    Ask an actor from another organization to change a known member ID. Predict the result, run it, and prove that knowing an ID does not grant permission.
    Apply a concept
    Compare two error designs against the same tests. Can a caller recognize forbidden, duplicate, and missing-member outcomes without inspecting message text?
    Scope & optional extensions
    Keep owner transfer, sign-up, and invitation email outside this first slice. If you expose an HTTP API later, derive the actor from an authenticated session; the fixture actor is only a test input.
  2. Next step Audited role changes Transactions, failure visibility, and boundaries
    Build this much
    Extend the membership module with a relational database and an initial migration. Store each successful role change and its audit entry in one transaction: actor, organization, member, previous role, new role, and operation ID.
    Your finish line
    Restart and find both the accepted role and its history. Reject the audit write during a role change: neither write commits. An unauthorized attempt creates no successful-change audit entry.
    Make one thing fail
    Inject a failure between the role update and audit insertion. Trace the operation through structured logs, then inspect the database. The evidence must explain both the failure and the rollback.
    Apply a concept
    Audit answers who changed what. Operational logs explain how execution went. Keep secrets out of both, and preserve useful error causes without exposing database details to API callers.
    Scope & optional extensions
    Add a read-only history query, then propagate context across an HTTP request and a queued job. Document what identifies the request, what groups the workflow, and which operation caused the job. Add a second adapter only when it serves a concrete test or integration.

Frontend 01 → 02

Make a design work.

Translate a visual reference into a usable screen. Then keep that screen understandable when data and timing change.

Before you startBasic HTML and CSS, plus components and local state in one framework. Use the framework and styling tools you want to practice.

  1. Start here One design, one complete screen Visual fidelity, responsive layout, and accessibility
    Build this much
    Choose a design you may reuse, such as a Figma Community settings or members screen, and credit its author. Implement its type, spacing, colors, and a few reusable controls. Use local fixture data and make one form work with local state.
    Your finish line
    Compare the implementation with the reference. Check a narrow screen, a wide screen, and widths between them. Long names fit, controls have labels, keyboard focus is visible, and submitting invalid input gives a useful message.
    Make one thing fail
    Replace the neat sample content with a very long name and an empty list, then complete the form using only the keyboard. Record the layout or interaction that broke and the fix.
    Apply a concept
    Use component composition to share behavior and design decisions. Check whether each component boundary makes the second screen easier to build and change.
    Scope & optional extensions
    Add a second screen that shares the controls, then change a spacing or color token. Explain what should change together and what belongs to one screen. A complete design system can grow from repeated needs.
  2. Next step A screen that handles uncertainty Async state, stale responses, and recovery
    Build this much
    Connect the screen to a controllable mock API. Add member search with loading, results, empty, and failed states. Let the mock delay, reject, or reorder requests. Give a failed read an explicit retry action.
    Your finish line
    Search for A, then B, and return A last. The screen still represents B. An empty response looks different from a failed request; retry succeeds; state changes remain understandable to keyboard and screen reader users.
    Make one thing fail
    Return search results in the wrong order, then fail the current request. Explain which response may update the screen and show a recovery path that keeps the current search input.
    Apply a concept
    Model the states and allowed transitions explicitly. A small API boundary lets the same screen use controlled fixtures and a real service later; the mock does not prove server authorization.
    Scope & optional extensions
    Add a role-edit form with pending, validation-error, and successful states. Preserve input after failure. Decide how a lost response is reconciled before retrying a mutation; a disabled button alone does not prevent duplicate server effects.
Keep the reasoning yours.

Use AI to help, then explain a decision, change a requirement, and reproduce a failure. Extract a reusable template after you have working behavior and a reason to share it.

Ready for a larger project?

Choose your pressure

Projects with a reason to go deeper.

These larger briefs are projects to grow into. Start with one product loop, then build toward the full acceptance checks in stages. Each capability has a job in the product.

Brief sketch / Messy input. Partial progress.

Bulk import & repair

Let a team upload a data file, preview validation errors, import accepted rows, and repair rejected ones.

Explore this direction

First useful slice

One file format, one record type, a preview, a durable import job, and a row-level results screen.

Where the depth comes from

Give each workspace its own imports and permissions. Checkpoint batches, enforce upload and storage quotas, and give each row a stable identity for retries. Migrate existing imports when the validation schema changes.

Break it on purpose

Kill the worker after a batch commits but before its checkpoint updates. Also submit a malformed row and the same file twice.

Bring back evidence

A rerun completes without duplicating accepted records. Show row errors, import lag, an alert for stalled work, and a trace from upload to batch.

Brief sketch / Time moves. Dependencies fail.

Scheduled report delivery

Let a team schedule a report, inspect its runs, and deliver the result to a controlled inbox.

Explore this direction

First useful slice

One report, one schedule per workspace, a test inbox, and a history of completed and failed runs.

Where the depth comes from

Separate who can view reports from who can change schedules. Persist each scheduled occurrence, cap work per tenant, and distinguish retrying a delivery from creating another run. Evolve schedules through a migration with old runs intact.

Break it on purpose

Restart the scheduler at a due time, return 500 from the inbox, and change the schedule while a run is queued.

Bring back evidence

Show one run identity per scheduled occurrence, bounded delivery attempts, and a documented missed-run policy. Alert on lateness and trace one report through delivery.

Brief sketch / Large files. Limited capacity.

Asset processing pipeline

Let a workspace upload images, generate a few variants in the background, and inspect processing progress.

Explore this direction

First useful slice

One supported image format, two variant sizes, authenticated uploads, and a durable processing history.

Where the depth comes from

Authorize originals and variants, persist job state, cap file size and worker concurrency, and reuse a stable variant identity on retry. Version the transform recipe and migrate existing metadata without losing originals.

Break it on purpose

Upload a corrupt file, stop a worker after a variant is written, and fill the processing queue.

Bring back evidence

Show that retries do not create duplicate variants, corrupt inputs fail clearly, and other workspaces keep making progress. Include queue-age alerts and a trace for a failed transform.

The engineering bar

Make each capability earn its place.

The full project briefs include authentication, authorization, persistence, background jobs, retries, idempotency, migrations, rate limiting, observability, and monitoring. The beginner exercises introduce a smaller set of responsibilities. Add each capability when the work gives it a purpose, and include a way to notice when its promises stop holding.

Build them in stages. An installed package or a checked box is the beginning; a repeatable demonstration is the evidence.

01

Authentication

Who is making this request?

What to demonstrate

Sign in, expire a session, revoke a credential, and show what access stops.

02

Authorization

What are they allowed to touch?

What to demonstrate

Use two accounts. Try reading and changing an object owned by the other one.

03

Persistence

What survives a restart?

What to demonstrate

Restart the app after it accepts work. Find the same data and explain the durability boundary.

04

Background jobs

Who owns work after the request ends?

What to demonstrate

Stop a worker mid-job. Restart it and show how the unfinished work is found.

05

Retries

Which failures deserve another attempt?

What to demonstrate

Make a dependency fail. Show the delay, attempt budget, and final outcome.

06

Idempotency

What if the same operation arrives twice?

What to demonstrate

Send duplicates concurrently. Show the stored operation and the downstream effect count.

07

Migrations

How does yesterday’s data meet today’s code?

What to demonstrate

Upgrade a populated database, verify old records, and rehearse the recovery plan.

08

Rate limiting

What happens when one caller sends too much?

What to demonstrate

Exceed a quota. Explain what is rejected and how another caller remains usable.

09

Observability

Can you explain one failed operation?

What to demonstrate

Connect its request, job, and attempts using structured logs and traces without exposing secrets.

10

Monitoring

How would you know users are stuck?

What to demonstrate

Trigger a defined alert, use its runbook, and show the signal returning to normal.

Build → break → explain

Give the project a chaos mode.

A reviewer should be able to choose a slow endpoint, a 500 response, a dropped connection, or malformed input. Make the failure repeatable. Show the affected state, the signal that exposed it, and the recovery path.

Explore the webhook failure drills Explore the feature flag failure drills Explore the offline inspection failure drills

The portfolio handoff

Leave something a reviewer can inspect.

01

A short failure demo

Show the product working, inject one fault, inspect the evidence, and recover. Let the viewer follow one event the whole way.

02

An architecture with promises

Mark the acceptance boundary, the job handoff, and the systems you control. State what survives a crash and where duplicate effects remain possible.

03

A measured workload

Publish the test command, environment, duration, limits, and observed results. Explain what the measurement does and does not establish.

04

A small incident report

Record the symptom, signal, diagnosis, mitigation, and prevention. Include a tradeoff you would revisit with more time or traffic.

05

A reproducible handoff

Provide local setup, synthetic fixtures, a migration rehearsal, and the fault reset procedure. Explain decisions and code you can defend, including work assisted by AI.