Back to all case studies

Case study

CodeHerWay: Building a coding education platform learners can trust

CodeHerWay brings lessons, quizzes, coding challenges, notes, and review tools into one place. Making those features available was only part of the work. The harder question was whether learners could trust what the platform told them: that their work was saved, that a checkpoint reflected understanding, and that opening the platform would not load tools they had not chosen to use. This case study follows three changes that made those promises more dependable.

  • A successful save can no longer be undone by an older queued write
  • 139 placeholder checkpoints withheld instead of scored
  • Entry dependency graph cut from 1,019 kB to 109 kB gzip
Project type
Self-paced web-development learning platform for women entering tech
Stack
React, TypeScript, Vite, Supabase, and Vercel
My role
Designer and builder. I owned delivery of all three changes and made the decision to withhold the generated quizzes. Implementation was AI-assisted where noted below.
Status
Pre-launch. Deployed and in active development, with production-readiness work still open. There are no learners yet, so there are no adoption or learning-outcome results.
Code access
Private repository. Pull request numbers below refer to it, and a code walkthrough is available on request.
Last verified
— every figure on this page was re-checked against the merged pull requests it cites.
CodeHerWay lesson workspace showing the Git and PRs course navigation, the six lesson steps, and the save and mark-complete controls
Current product, captured signed in on September 21, 2026: the lesson "Snapshots, Not Diffs" in the Git & PRs track. The workspace puts the objectives, lesson sequence, and save controls together.

Learner trust lens

For a beginner, completing a lesson, answering a question, or saving a bookmark should produce a result she can believe.

Three places in CodeHerWay fell short of that. Each one was a mismatch between what the interface implied and what the code underneath actually did. The work stayed inside the existing architecture: reproduce a specific failure, repair the path that caused it, and confirm the tests could detect the original defect.

Saved means saved

A failed save could stay queued and later undo a newer successful action.

A checkpoint means something

Template-generated quizzes could reward recognizing an answer pattern without testing the lesson.

The first visit loads what you chose

Opening the platform downloaded the code editor's dependency graph, although the editor was meant to load later.

Evidence Snapshot

What shipped

  • A confirmed successful save retires older conflicting queued writes for the same record
  • Generated placeholder checkpoints are withheld and excluded from quiz-coverage reporting
  • Vite's shared preload helper moved to its own chunk, removing Monaco from the entry graph

Where the numbers come from

  • Every figure is quoted from the verification section of a merged pull request
  • They are dated build and audit measurements, not monitored metrics
  • The quiz counts were not independently recounted from the question source for this page

Honest limits

  • No completion, retention, or learning-outcome data exists yet
  • The save fixes are verified by automated tests; the offline flow was not run by hand
  • The payload reduction is not a measured load-time improvement
  • The repository is private, so the pull requests cannot be opened from this page

1. Make a Successful Save Stay Saved

Consider a learner who unmarks a lesson while her connection is unreliable. That request fails, so the platform queues it for retry. She then marks the lesson complete again, and this save succeeds. Before the fix, reconnecting could replay the old removal and erase the completion. The interface had confirmed an action that a stale background operation could later reverse. Bookmarks had the same failure pattern.

The repair clears obsolete queued writes after a newer write for the same record succeeds. It first covered direct progress writes (pull request 1020, August 17, 2026), then extended to the lesson-page buttons, which save through a different path (pull request 1211, September 17, 2026).

  • Constraint

    On a failed save, the queued item is the retry. Dropping it would lose the write entirely, which is the opposite data loss and just as bad.

  • Decision

    Keep the retry system. Clear queued work only after a confirmed success, and only for the same record, identified by the queue's own resource keys.

  • Tradeoff

    Clearing the whole queue would have been simpler, but it could discard unrelated work that still needed to sync. Precision cost more code and more tests.

The important detail was deciding which operations were actually obsolete. A lesson bookmark and a concept bookmark inside that lesson are separate records, so clearing one must not discard the other, and there is a test for that refusal. Older lesson-key spellings that a browser might still hold also had to be matched.

The original reproduction failed before the first repair and passed afterward. For the second repair, each production change was reverted in place to confirm its tests failed without it. One revert initially passed, which exposed an untested line; a test was added and the revert then failed as it should.

CodeHerWay progress snapshot with lessons saved, current state, review due, and a recommended next step
Current product context: the progress snapshot shows saved reading progress and a recommended next step. It shows what learners depend on; it does not reproduce the historical reconnect failure.

What this does and does not promise: covered progress and bookmark writes can recover in the same browser without an obsolete queued operation undoing a later successful save. That is verified in automated tests against the real write queue. The reconnect flow was not exercised by hand, and this is not a claim of universal offline or cross-device recovery.

2. Protect Mastery Evidence From Generated Quizzes

"I'd rather show no checkpoint than record a false one."

I withheld the generated checkpoints because a quiz that can be passed by recognizing an answer pattern still produces a score. The submission path treated that score as ordinary quiz evidence, and the platform used quiz scores as a signal of mastery.

The affected banks covered Next.js, React + TypeScript, and TypeScript Foundations. Their templates lacked the lesson-specific context needed to write plausible wrong answers. I did not think rewriting those templates would fix what the quizzes measured, even if it made the coverage report look better. Changing the label was also not enough: calling a quiz "generated" changes what the learner is told, but the score and review-card write path did not distinguish those banks from authored ones.

So the checkpoint is withheld until authored questions exist. That accepts visible gaps in the assessment experience and a smaller reported inventory, in exchange for keeping those checkpoints out of the normal score and review-card paths.

The decision is enforced where the quiz would render. The layout checks the bank's own "generated" marker and omits the whole checkpoint, heading included. Because the quiz never mounts, its submission handler cannot run. The gate reads the marker rather than a course list, so a checkpoint returns lesson by lesson as an authored bank replaces a generated one.

  • Before

    The quiz audit counted 316 lessons across 8 courses as having active quiz coverage, and reported that coverage as complete.

  • After

    177 lessons across 5 courses. 139 generated checkpoints and their 417 template questions are reported separately as withheld placeholders.

  • Not claimed

    These counts describe the assessment inventory, not learner achievement. No quiz questions or saved learner records were deleted. The 417 questions are unchanged.

A database check recorded the same day found no score or review-card rows matching any generated quiz. The outcome was preventive: a path that could record misleading evidence was blocked before anyone used it. The audit finding was closed on that basis, with the closure note stating plainly that the three courses still do not have authored quizzes. Writing those questions is tracked separately as curriculum work (pull requests 1218 and 1222, September 17, 2026).

CodeHerWay learning transcript separating reading progress, recall checks, application proof, and review health
Current product, September 21, 2026: the learning transcript separates four kinds of learning evidence. The displayed values are one account's state, not a before-and-after capture of this change.

Remaining limitation: the quiz submission hook still writes scores and queues review cards without checking the generated marker. The rendering gate is the current protection. A future surface that mounts a generated bank and submits through that hook could reintroduce the problem, so a check in the write path itself is an open engineering follow-up.

3. Load the Editor When the Learner Needs It

CodeHerWay includes an in-browser coding environment, but a visitor should not need its full editor payload just to open the platform.

An investigation into a small bundle-headroom item found something much larger. Vite's shared preload helper, which every chunk with a dynamic import depends on, had been placed inside a Monaco language chunk. Because the app entry needed that helper, it pulled in the editor's dependency graph with it.

The existing budget check did not reveal the cost. It measured the assets referenced by the entry HTML, while the extra dependencies arrived through static imports between JavaScript chunks. The check reported about 109 kB while the real figure was over 1,000 kB.

  • Before

    Entry dependency graph: 1,019.42 kB gzip JavaScript across 13 chunks, 7 of them Monaco.

  • After

    109.10 kB across 7 chunks, none of them Monaco. The fix was one rule in the build configuration that gives the helper its own 0.66 kB chunk.

  • Not claimed

    That is about 89.3% less gzip JavaScript in the measured entry graph. It is a payload reduction, not a measured 89.3% improvement in loading time.

The small configuration change depended on a larger investigative step: checking what the dependency graph actually contained instead of accepting a passing budget result (pull request 880, July 26, 2026).

CodeHerWay JavaScript challenge with a code editor, preview pane, and Run Tests control
Current product: the "Personalized Theme Console" challenge opens the editor beside its preview and five test checks. The capture shows the starter state; no solution was added. The payload comparison above comes from the pull request, not this screenshot.

Left open: strengthening the budget check to walk static imports from the entry was deferred as a separate decision, because it changes what CI measures. The fix removed the observed problem. It did not make every similar regression impossible.

My Role

I am CodeHerWay's designer and builder, and the pull requests described here were opened and merged under my account.

  • Save reliability. I owned delivery of the save-queue fixes across direct writes and lesson-page actions. The work preserved the existing retry system while preventing older queued operations from undoing newer successful saves.
  • Assessment. Withholding the generated quizzes was my decision, for the reasons given in that chapter.
  • Editor loading. I owned delivery of the Monaco loading fix, with AI-assisted implementation. The work traced an unexpected dependency from the app entry to the editor and isolated Vite's shared preload helper.

Results

  • Newer successful lesson and bookmark saves supersede older conflicting queued writes.
  • Generated placeholder checkpoints are withheld, which prevents submissions through those surfaces, and are excluded from quiz-coverage reporting.
  • The measured initial JavaScript dependency graph is substantially smaller, with Monaco removed from it.

These results support a case for stronger implementation and more accurate product promises. They do not demonstrate higher completion rates, better retention, or improved learning outcomes.

Accessibility Considerations

None of the three changes altered keyboard, focus, or screen-reader behavior, and this page makes no new accessibility claim. One detail is relevant: a withheld checkpoint is removed together with its eyebrow and heading, so no empty section is left in the page. The platform's accessibility coverage is described on the platform overview.

Performance Considerations

  • The entry-graph figures are build measurements taken on July 26, 2026. Later work has changed the bundle, so they describe that fix, not today's build.
  • No Lighthouse or field data is reported here. Browser performance under consistent device and network conditions has not been captured.
  • The first save-queue change adds one local-storage read and a filter on the success path of a write that has already made a network round trip.

What the Work Revealed

A passing check is useful only when it measures the behavior that matters. The save queue needed tests spanning a failure, a later success, and a replay. Quiz assessment needed to protect the meaning of a recorded score, not only the accuracy of a coverage report. Performance measurement needed to account for dependencies beyond the assets listed in the HTML.

The common lesson was to make the product claim concrete enough to challenge. Can a successful save be undone? Can a quiz be passed without understanding? Does the first page really defer the editor?

What I Would Improve Next

  • Replace the withheld placeholder questions with authored assessments.
  • Protect quiz writes in the submission path, independent of which surface renders the bank.
  • Strengthen the bundle budget check so it walks static imports from the entry.
  • Run the offline and reconnect save flow by hand, then observe real learners using it.
  • Capture browser performance under consistent device and network conditions.