Skip to content

refactor: close the structural findings from the architecture review - #86

Merged
Isma-L154 merged 2 commits into
mainfrom
refactor/review-depth
Sep 11, 2026
Merged

Isma-L154 merged 2 commits into
mainfrom
refactor/review-depth

Conversation

@Isma-L154

Copy link
Copy Markdown
Owner

Closes the rest of #81 — F2, F3 (remainder), F5, F6, F7 — and reaches a different conclusion on F9 than the review did.

F2 · The e2e suite could run against another application

It listened on 5173, the port every Vite project takes by default, and reuseExistingServer adopted whatever was already there without checking whose it was. That is how seventeen tests once failed against a stranger's HTML, pointing at robots.txt and security headers rather than at anything real. The dangerous direction is the other one: a reused server running stale code reporting green.

Now a port nothing else defaults to, plus --strictPort — without it Vite answers a busy port by quietly moving to the next one, which would leave the suite pointed at nothing.

F3 · Six exports made private

Each checked against its spec first, because several siblings are public precisely so they can be unit-tested and those must stay: DELETION_WARNING_DAYS, watchableSeasons, MAX_SEEDS, MAX_RAILS, MAX_RAIL_ITEMS, SHARE_SCOPES, CALLBACK_PATH.

F5 · Components no longer type against the database schema

WatchlistCard and ContinueWatching took WatchlistItem from $lib/server/db/schema. Type-only, so nothing ever shipped to the browser — but presentation depended on the persistence shape, and adding a column changed the type a card saw.

They now take SavedTitle, owned by the domain: what the rules need, plus the three things only a rendering cares about. The database row satisfies it structurally, so nothing converts anything. The proof it was real coupling: the card's own fixture lost userId, overview and addedAt — three fields it never used and should never have been asked for.

F6 · watchlistActions split

485 lines and four reasons to change → the titles (list.ts), the links that publish them (sharing.ts), the account's own settings (settings.ts), spread back into one map in index.ts.

The route contract is untouched: all 55 action tests pass unedited, and the spec's import('./actions') resolves to the new index on its own.

F7 · tmdb.ts split

612 lines and six endpoint families whose only genuinely shared parts are the fetch and two raw response shapes. Those are client.ts; search, seasons, details, recommendations, trending and people each have their own file behind an index that re-exports the same public surface. No import anywhere else changed.

F9 · The review's number was wrong, and so was its conclusion

I have to correct this rather than act on it. The 46% figure came from a counter with a bug: entering an HTML comment <!-- … --> it only looked for */ to exit, so it swallowed the rest of every Svelte file as comment. The "82% files" were an artifact of that.

Measured correctly:

claimed actual
Whole codebase 46% 35%
Worst files MediaCard.svelte 82% domain/filmography.ts 65%

And the real top files are not components — they are filmography.ts, db/schema.ts, title-url.ts, domain/share.ts: files that document why a tuning constant is the value it is (why five credits, why twenty, why eight people). That is the kind worth keeping, and a sweep would have destroyed it.

A targeted hunt for comments that merely restate their code found essentially nothing beyond SvelteKit's own scaffolding in app.d.ts.

So no sweep. What was genuinely bloated was prose in markup, and MediaCard had two thirteen-line essays. Both are now five and six lines with every fact intact — the blur ban still carries its measurement (794ms → 392ms over 300 frames, 154 compositing layers), the clamp note still names both reasons it cannot move to the button. 32 comment lines removed, and the file is the better for it; the codebase moved 36% → 35%, which is the honest size of this.

Verified

  • npm run check — 0 errors, 0 warnings; npm run lint clean
  • 488 unit tests pass, none edited — that is the point of a mechanical split
  • 17 e2e pass, on the new port, without touching the dev server on 5173
  • Every TMDB family exercised over real HTTP after the split: search, trending, details+seasons, person, and the public title page
  • Every one of the 12 actions exercised after the split, including the bodyless ones, plus the 401 guard on all three modules and the "isn't out yet" rule still refusing
  • Driven in a browser: list, progress bars, Continue Watching, share panel, release pill. No console errors.

Four of the five that were left open in #81. The fifth is comment
density, which is a house-style question and does not belong in the same
commit as a set of mechanical moves.

The e2e suite could run against somebody else's application. It listened
on 5173 — the port every Vite project takes by default — and
`reuseExistingServer` adopted whatever was already there without checking
whose it was. That is how seventeen tests once failed against a
stranger's HTML, pointing at robots.txt and security headers rather than
at anything real; the dangerous direction is the other one, where a
reused server running stale code reports green. It now has a port nothing
else defaults to, and `--strictPort` so Vite cannot answer a busy port by
quietly moving to another one.

Six exports were only ever used inside their own module and are now
private. Each was checked against its spec first: several siblings are
public precisely so they can be unit-tested, and those stay.

Two components were typed against the database schema. The import was
type-only, so nothing ever shipped to the browser, but it pointed
presentation at the persistence shape — adding a column changed the type
a card saw. They now take `SavedTitle`, which the domain owns: what the
rules need, plus the three things only a rendering cares about. The
database row satisfies it structurally, so nothing converts anything, and
the card's own test fixture lost `userId`, `overview` and `addedAt` —
three fields it never used and should never have been asked for.

`watchlistActions` was one 485-line object with four reasons to change.
Split into the titles, the links that publish them, and the account's own
settings, spread back into one action map so the route contract is
untouched: every one of the 55 action tests passes unedited.

`tmdb.ts` was 612 lines and six endpoint families whose only genuinely
shared parts are the fetch and two raw response shapes. Those are now
`client`, and search, seasons, details, recommendations, trending and
people each have their own file behind an index that re-exports the same
public surface. No import anywhere else changed.
The file carried two thirteen-line essays in its markup. Both record
something worth keeping — a measurement, and a pair of CSS mechanics that
are not guessable — and both said it at four times the necessary length.

They are now five and six lines. Nothing factual is lost: the blur block
still carries the numbers that justify the ban (794ms -> 392ms over 300
frames, 154 compositing layers on a grid of 77) and the clamp block still
names both reasons the clamp cannot move to the button.

Four prop docs and two inline notes got the same treatment. The LCP
figures stay; the retelling around them does not.
@Isma-L154
Isma-L154 merged commit 4d89c9e into main Sep 11, 2026
3 checks passed
@Isma-L154
Isma-L154 deleted the refactor/review-depth branch September 11, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant