Skip to content

Commit 161c1ad

Browse files
ManuelMaduclaude
andcommitted
Humanize README prose to remove AI-writing tells
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ce5c228 commit 161c1ad

1 file changed

Lines changed: 38 additions & 38 deletions

File tree

README.md

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44

55
**Live:** https://qwertide.azurewebsites.net
66

7-
A browser-based typing-speed game built end to end in **C# / .NET 8**. A passage
8-
appears, you type it, and Qwertide tracks your words-per-minute and accuracy live —
9-
then drops your run onto a persistent, API-backed leaderboard.
7+
A browser-based typing game written end to end in **C# / .NET 8**. A passage shows
8+
up, you type it, and Qwertide tracks your words per minute and accuracy as you go,
9+
then saves your run to a leaderboard backed by a real API.
1010

11-
The entire front-end and game loop are **Blazor WebAssembly** (the scoring engine
12-
is pure C#; the only JavaScript is a 22-line caret-positioning helper). The
13-
leaderboard is served by an **ASP.NET Core Web API** backed by **EF Core + SQLite**.
14-
In production both ship as a **single Azure App Service**. It was built as a
15-
focused, production-minded portfolio piece for a Junior C#/.NET role.
11+
The front-end and game loop are all **Blazor WebAssembly**. The scoring is pure C#;
12+
the only JavaScript is a 22-line helper that positions the caret. The leaderboard
13+
runs on an **ASP.NET Core Web API** with **EF Core and SQLite** behind it. In
14+
production the two ship together as one **Azure App Service**. I built it as a
15+
portfolio piece for a junior C#/.NET role, and tried to keep it small but
16+
production-minded rather than a pile of half-finished features.
1617

1718
---
1819

@@ -42,13 +43,13 @@ focused, production-minded portfolio piece for a Junior C#/.NET role.
4243

4344
## Overview
4445

45-
Qwertide is a full-stack, single-page typing test. The interesting engineering is
46-
deliberately *not* in the UI: the words-per-minute and accuracy math is isolated in
47-
a pure, dependency-free domain class so it can be unit-tested directly, and the
48-
leaderboard is a small but properly-hardened REST API rather than a localStorage
49-
hack. The project's goal is to demonstrate the complete C#/.NET stack — WASM
50-
front-end, Web API, ORM with migrations, a tested domain layer, CI, and a public
51-
cloud deployment — in one coherent, honestly-scoped app.
46+
Qwertide is a full-stack, single-page typing test. Most of the actual engineering
47+
sits behind the UI rather than in it. The WPM and accuracy math lives in a pure,
48+
dependency-free class so it can be unit-tested on its own, and the leaderboard is a
49+
small REST API with real input validation and rate limiting instead of a
50+
localStorage shortcut. The point of the project is to show the whole C#/.NET stack
51+
working together: a WASM front-end, a Web API, an ORM with migrations, a tested
52+
domain layer, CI, and a live cloud deploy.
5253

5354
## Key features
5455

@@ -90,16 +91,16 @@ without a browser and the client depending on the API only through an interface.
9091

9192
**Key decisions**
9293

93-
- **Pure domain layer.** All metric math lives in `TypingSession` as static,
94-
UI-free functions, so the test project references it directly and the Blazor
95-
component owns only rendering.
96-
- **Interface-driven leaderboard.** The UI codes against `ILeaderboardService`;
97-
the active implementation (`ApiLeaderboardService`) is swapped in via DI without
98-
any UI changes. A second `localStorage` implementation exists to demonstrate the
99-
abstraction (it is not currently wired in as a runtime fallback).
94+
- **Pure domain layer.** All the metric math lives in `TypingSession` as static,
95+
UI-free functions, so the test project can reference it directly and the Blazor
96+
component only has to worry about rendering.
97+
- **Interface-driven leaderboard.** The UI codes against `ILeaderboardService`, and
98+
the active implementation (`ApiLeaderboardService`) gets swapped in via DI without
99+
touching the UI. There's also a second `localStorage` implementation that shows
100+
the abstraction works; it isn't currently wired in as a runtime fallback.
100101
- **Single-service hosting.** In production the API serves the published WASM
101-
client and falls back to `index.html` for client-side routes, so there is one
102-
deployable, one origin, and no production CORS.
102+
client and falls back to `index.html` for client-side routes. That leaves one
103+
deployable, one origin, and no CORS to worry about in prod.
103104

104105
### The scoring engine
105106

@@ -113,9 +114,9 @@ TypingSession.GrossWpmFor(charsTyped: 50, elapsedSeconds: 60); // -> 10
113114
TypingSession.AccuracyFor(correctKeystrokes: 45, totalKeystrokes: 50); // -> 90
114115
```
115116

116-
`CountKeystrokes` counts *every* character committed in a single input event
117-
(a fast typist or IME can commit several between ticks), so the accuracy
118-
denominator is never under-counted.
117+
`CountKeystrokes` counts every character committed in a single input event, since a
118+
fast typist or an IME can commit several between ticks. That way the accuracy
119+
denominator never gets under-counted.
119120

120121
## Tech stack
121122

@@ -186,8 +187,8 @@ that break naive implementations: zero elapsed time, all-errors, empty input,
186187
multi-character input events, and derived-metric state. Tests target the pure
187188
domain layer directly, so they run fast and need no browser or HTTP host.
188189

189-
**Scope, stated honestly:** test coverage is the scoring engine. API/controller
190-
integration tests, component tests, and end-to-end tests are not yet present (see
190+
What's actually covered is the scoring engine. There are no API/controller
191+
integration tests, component tests, or end-to-end tests yet (see
191192
[Future improvements](#future-improvements-not-yet-implemented)).
192193

193194
## CI/CD
@@ -212,9 +213,8 @@ there is no automated CD pipeline yet.
212213
Live on **Azure App Service** (Linux) as a single service: the ASP.NET Core API
213214
hosts both the leaderboard endpoints and the published Blazor client. The SQLite
214215
file lives on the persistent `/home` share, so scores survive restarts and
215-
redeploys. The complete, reproducible runbook — resource creation, connection
216-
string, HTTPS-only, and the publish/zip/deploy commands — is in
217-
[DEPLOY.md](DEPLOY.md).
216+
redeploys. The full runbook (resource creation, connection string, HTTPS-only, and
217+
the publish/zip/deploy commands) is in [DEPLOY.md](DEPLOY.md).
218218

219219
## Performance considerations
220220

@@ -309,10 +309,10 @@ overridable by environment variables (double-underscore notation).
309309
## Engineering tradeoffs
310310

311311
- **No authentication.** A typing game's leaderboard doesn't need accounts, so the
312-
API is anonymous. The consequence — accepted deliberately — is that scores are
313-
client-submitted and not server-verified; `[Range]` validation blocks absurd
314-
values but not a plausible fake. Real anti-cheat would require server-side
315-
gameplay validation or auth, which is out of scope for this piece.
312+
API is anonymous. The trade-off, which I took on purpose, is that scores are
313+
client-submitted and not server-verified. `[Range]` validation blocks absurd
314+
values but won't catch a plausible fake. Real anti-cheat would mean server-side
315+
gameplay validation or auth, and that's out of scope here.
316316
- **SQLite over a managed SQL service.** Zero-cost, zero-ops, and ideal for a
317317
single-instance portfolio app, at the cost of horizontal scalability (see below).
318318
- **Migrate-on-startup.** Convenient for a one-instance deploy; a controlled
@@ -322,7 +322,7 @@ overridable by environment variables (double-underscore notation).
322322

323323
## Known limitations
324324

325-
These are real gaps, listed so the scope is unambiguous:
325+
The gaps, spelled out so the scope is clear:
326326

327327
- Scores are **not authenticated or server-verified** (spoofable by design).
328328
- **SQLite is single-node** — the app cannot currently scale out to multiple App
@@ -340,7 +340,7 @@ These are real gaps, listed so the scope is unambiguous:
340340

341341
## Future improvements (not yet implemented)
342342

343-
Prioritised, and clearly separate from what exists today:
343+
Roughly in priority order. None of these exist yet:
344344

345345
1. **API integration tests** with `WebApplicationFactory`, plus bUnit component
346346
tests and a Playwright E2E happy-path; publish coverage from CI.

0 commit comments

Comments
 (0)