20 KiB
Maintainability Review - Pick'n'Play
A) Executive summary
This codebase is functional and reasonably tested on backend behavior, but long-term change safety is currently limited by concentration risk (god modules), hidden side effects in read paths, and drifting operational contracts.
Progress update (as of February 6, 2026):
- Completed: phase reads are side-effect free with explicit reconciliation on write routes (
Endpoints/EndpointHelpers.cs:37,Endpoints/EndpointHelpers.cs:61,Endpoints/StateEndpoints.cs:62). - Completed: admin auth docs aligned to account-based admin sessions (
API.md:3). - Completed: build/test guardrails added (
.github/workflows/ci.yml) and root ownership/setup docs added (README.md:1). - Completed: backend validators centralized for suggestions and auth (
Endpoints/SuggestionValidator.cs:7,Endpoints/AuthValidator.cs:11). - Completed: request safety hardened for redirects and forwarded headers (
Program.cs:40,Program.cs:104,Endpoints/EndpointHelpers.cs:105,GameList.Tests/HelperTests.cs:121,GameList.Tests/HelperTests.cs:219).
Top 5 maintainability risks (priority order):
- Frontend module concentration and global coupling (Critical)
wwwroot/js/ui.jsis still the dominant hotspot and owns rendering, validation, modal orchestration, admin flows, and vote logic.- Hidden module coupling through globals:
wwwroot/js/data.js:131-wwwroot/js/data.js:134, pluswindowcallbacks consumed inwwwroot/js/ui.js:473,wwwroot/js/ui.js:696,wwwroot/js/ui.js:1009. - Impact: hard-to-debug regressions and fragile refactors in UI workflows.
- Rule duplication still present between backend/frontend validations (High)
- Suggestion validation is centralized on the backend (
Endpoints/SuggestEndpoints.cs:45,Endpoints/SuggestEndpoints.cs:133,Endpoints/SuggestionValidator.cs:7) but frontend still duplicates parts (wwwroot/js/ui.js:648,wwwroot/js/ui.js:1019). - Auth validation is centralized on the backend (
Endpoints/AuthEndpoints.cs:18,Endpoints/AuthEndpoints.cs:65,Endpoints/AuthValidator.cs:11) while frontend length checks remain duplicated (wwwroot/app.js:92,wwwroot/app.js:121). - Impact: inconsistent behavior and repeated fixes across server/client.
- High-change, high-complexity frontend hotspots (High)
- Git churn:
wwwroot/app.js(76 changes),wwwroot/js/ui.js(55),wwwroot/js/i18n.js(50). wwwroot/js/ui.jsis 1123 lines and owns rendering, validation, modal orchestration, admin flows, and vote logic.- Hidden module coupling through globals:
wwwroot/js/data.js:131-wwwroot/js/data.js:134, pluswindowcallbacks consumed inwwwroot/js/ui.js:473,wwwroot/js/ui.js:696,wwwroot/js/ui.js:1009. - Impact: every UI change risks regressions outside its feature area.
- Service-layer extraction is still pending in large endpoint files (High)
- Endpoint lambdas still own orchestration and persistence logic in
Endpoints/SuggestEndpoints.cs,Endpoints/AdminEndpoints.cs,Endpoints/VoteEndpoints.cs, andEndpoints/ResultsEndpoints.cs. - Impact: high cognitive load and slower, riskier feature changes.
- Static-analysis and frontend lint guardrails remain incomplete (Medium)
- Build/test CI exists (
.github/workflows/ci.yml) and project content rules are fixed (GameList.csproj:17-GameList.csproj:21), but analyzers/lint/format gates are still absent. - Impact: regressions and style drift can still slip through.
Where to start (recommended sequence):
- Start with P0 tasks that reduce surprise and operational risk: phase-read side effects, auth contract drift, build hygiene, and validation centralization.
- Apply these guiding principles:
- Keep reads pure and writes explicit.
- Consolidate business rules in one place per rule.
- Reduce module fan-in/fan-out before adding features.
- Add small guardrails (build warnings, CI gates) before larger refactors.
Assumptions and validation:
- Assumption A1: local git history reflects meaningful churn/risk. Validate by comparing with remote
main/PR stats if different branch practices exist. - Assumption A2: build warnings are reproducible environment signals, not one-off file locks. Validate by running clean build in CI and on a fresh clone.
B) Maintainability map
Major modules/components and responsibilities:
Program.cs: app bootstrap, infrastructure wiring, middleware order, DB migration on startup, route registration.Endpoints/*.cs: HTTP endpoint definitions and most business logic (auth, phase, suggestions, votes, results, admin).Endpoints/EndpointHelpers.cs: cross-cutting helper hub (auth lookup, phase alignment, URL checks, image probing, link graph utilities).Infrastructure/*.cs: filters/middleware/auth helpers (AdminOnlyFilter, phase filters, cookie identity helpers, global exception wrapper).Data/AppDbContext.cs: EF Core model and persistence mappings.Domain/*.cs: entity data structures (Player,Suggestion,Vote,AppState,Phase).wwwroot/app.js+wwwroot/js/*.js: frontend orchestration, shared state, API calls, rendering, i18n, effects.GameList.Tests/*.cs: integration-heavy endpoint tests plus helper/unit tests.scripts/*.ps1: deployment automation.
Boundary quality:
- Backend boundaries are leaky: endpoint layer owns domain rules, persistence orchestration, and security checks directly.
Infrastructuredepends onEndpoints(Infrastructure/PhaseRequirementFilter.cs:3) whileEndpointsdepend back onInfrastructure(Endpoints/EndpointHelpers.cs:22), creating conceptual circular ownership.- Frontend boundaries are leaky:
ui.jsmixes rendering, form validation, and server mutation calls; shared mutable state is directly mutated across modules.
Worst coupling points:
Endpoints/EndpointHelpers.cs(84 call sites): de facto god module.wwwroot/js/ui.js+ globalstateobject (131 direct state references across JS modules).- Suggestion and phase workflows span endpoint functions, helper functions, filters, and duplicated client-side checks.
- Operational behavior is split across
API.mdand runtime filters with contradictory contracts.
C) Critical task list
[P0][Done] Make phase reads side-effect free and move reconciliation to explicit writes
- Problem: Severity
Critical, CategoryArchitecture. Read endpoints/filters previously relied on mutating phase reads. Impact: unsafe refactors and non-deterministic behavior. - Evidence:
Endpoints/EndpointHelpers.cs:37,Endpoints/EndpointHelpers.cs:61,Endpoints/StateEndpoints.cs:20,Infrastructure/PhaseRequirementFilter.cs:17,Endpoints/ResultsEndpoints.cs:26,GameList.Tests/StateTests.cs:236,GameList.Tests/FiltersTests.cs:55. - Recommendation: Split into
GetCurrentPhaseAsync(pure read) and explicitReconcilePhaseAsync(write command). Run reconciliation only on intentional transition points (admin toggle, phase change commands, migration job), not on GET paths. - Acceptance criteria (testable): GET
/api/stateand GET/api/menever callSaveChangesAsync; integration tests verify no phase mutations occur during read-only requests; filters perform one phase check path without side effects. - Effort / Risk:
M / Med. - Dependencies (if any): none.
[P0][Done] Repair admin auth contract drift across code, docs, and smoke automation
- Problem: Severity
High, CategoryDocumentation/Tooling. Documentation and smoke script specify header-based admin auth, runtime requires authenticated admin cookie. Impact: runbooks are misleading during incidents/releases. - Evidence:
API.md:3,Infrastructure/AdminOnlyFilter.cs:12. - Recommendation: Make one contract authoritative (account-based admin role), update docs to follow it, and add one integration smoke test path that validates real auth flow.
- Acceptance criteria (testable):
API.mdno longer referencesX-Admin-Key; one automated test verifies admin route rejection/acceptance behavior. - Effort / Risk:
S / Low. - Dependencies (if any): none.
[P0][Done] Eliminate build instability and warning noise from project layout/content rules
- Problem: Severity
High, CategoryTooling. Current build emits MSB3026 warnings and recursive copy paths under test output, reducing trust in build outputs and masking real issues. - Evidence: content exclusions now include
ContentinGameList.csproj:17-GameList.csproj:21; CI gate added in.github/workflows/ci.yml. - Recommendation: Ensure test assets are fully excluded from web content pipeline (or move tests outside web project root), then enforce clean build (
0 warnings) in CI. - Acceptance criteria (testable):
dotnet build GameList.slnemits zero warnings; publish output contains only expected runtime artifacts; CI fails on warning regressions. - Effort / Risk:
M / Med. - Dependencies (if any): none.
[P0][Partial] Centralize validation rules to stop backend/frontend drift
- Problem: Severity
High, CategoryComplexity/Duplication. Validation rules are duplicated in multiple backend endpoints and frontend forms. Impact: inconsistent behavior and repeated fixes. - Evidence: backend centralized in
Endpoints/SuggestEndpoints.cs:45,Endpoints/SuggestEndpoints.cs:133,Endpoints/SuggestionValidator.cs:7,Endpoints/AuthEndpoints.cs:18,Endpoints/AuthEndpoints.cs:65,Endpoints/AuthValidator.cs:11; frontend duplicates remain inwwwroot/js/ui.js:648,wwwroot/js/ui.js:1019,wwwroot/app.js:92. - Recommendation: Extract backend validators (e.g.,
SuggestionValidator,AuthValidator) and reuse in create/update paths; simplify frontend to UX-only prechecks and rely on server responses for source-of-truth. - Acceptance criteria (testable): create/update share one backend validator path; tests cover validator once and both endpoints; frontend no longer re-implements server-only security rules.
- Effort / Risk:
M / Med. - Dependencies (if any): none.
[P0][Done] Harden request safety defaults (forwarded headers and redirect handling)
- Problem: Severity
High, CategorySecurity. Forwarded headers are trusted without explicit proxy/network allowlist, and image validation likely follows redirects despite "no redirects" policy. - Evidence:
Program.cs:40,Program.cs:70,Program.cs:104,Endpoints/EndpointHelpers.cs:105,GameList.Tests/HelperTests.cs:121,GameList.Tests/HelperTests.cs:219,IIS.md:17. - Recommendation: Configure known proxies/networks for forwarded headers; enforce
AllowAutoRedirect = falsein image validation client and add tests for redirect-chain and private-host edge cases. - Acceptance criteria (testable): integration tests prove redirected URLs are rejected; forwarded header spoofing test fails when source is untrusted; documentation updated with trusted proxy requirements.
- Effort / Risk:
M / Med. - Dependencies (if any): none.
[P1] Extract service-layer workflows from endpoint lambdas
- Problem: Severity
High, CategoryArchitecture. Endpoint files contain business orchestration, persistence, and policy logic inline; large lambdas are hard to reason about and reuse. - Evidence:
Endpoints/SuggestEndpoints.cs:43,Endpoints/AdminEndpoints.cs:105,Endpoints/VoteEndpoints.cs:35,Endpoints/ResultsEndpoints.cs:30. - Recommendation: Introduce focused application services (
SuggestionService,VoteService,AdminWorkflowService) and keep endpoints as transport adapters. - Acceptance criteria (testable): endpoint handlers reduced to routing + DTO mapping + service calls; domain rule tests target service methods directly; endpoint tests remain green.
- Effort / Risk:
L / Med. - Dependencies (if any): P0 phase-read cleanup recommended first.
[P1] Decompose frontend UI monolith and remove window cross-module hooks
- Problem: Severity
High, CategoryArchitecture/Complexity. UI logic is concentrated inui.js; globalwindowcallbacks hide dependencies and increase accidental complexity. - Evidence:
wwwroot/js/ui.js:390,wwwroot/js/data.js:131,wwwroot/js/data.js:134,wwwroot/js/ui.js:473,wwwroot/js/ui.js:1009. - Recommendation: Split UI by feature (
suggestions-ui,votes-ui,admin-ui,modals-ui) and use explicit imports/events instead ofwindowglobals. - Acceptance criteria (testable): no feature code depends on
window.refreshPhaseData/window.loadVoteData; module boundaries documented; smoke behavior unchanged. - Effort / Risk:
L / Med. - Dependencies (if any): none.
[P1] Replace uncontrolled polling with serialized refresh scheduling
- Problem: Severity
Medium, CategoryReliability/Complexity. A fixed 4-second interval can overlap async refreshes and race state updates. - Evidence:
wwwroot/app.js:293-wwwroot/app.js:297,wwwroot/js/data.js:82. - Recommendation: Add a scheduler that avoids overlapping refreshes (single-flight), pauses when tab hidden, and supports explicit event-triggered refresh.
- Acceptance criteria (testable): at most one in-flight refresh at any time; no duplicate toasts/state flicker during slow network simulation; tests for scheduler behavior.
- Effort / Risk:
M / Low. - Dependencies (if any): none.
[P1] Remove legacy/dead paths to reduce cognitive load
- Problem: Severity
Medium, CategoryOther. LegacyRevealphase and dead UI hooks remain in active code, increasing confusion. - Evidence:
Domain/Phase.cs:6,Endpoints/StateEndpoints.cs:107,wwwroot/js/data.js:30,wwwroot/js/ui.js:156,wwwroot/js/ui.js:1191. - Recommendation: Remove obsolete phase enum/value handling and dead UI references (
all-suggestions,nav-vote-next). - Acceptance criteria (testable): no references to removed phase/UI ids remain; tests validate expected phase transitions only (
Suggest,Vote,Results). - Effort / Risk:
S / Low. - Dependencies (if any): P0 phase cleanup.
[P1] Make write workflows transaction-consistent and explicit
- Problem: Severity
Medium, CategoryCorrectness/Architecture. Several multi-step state changes rely on multiple DB commands without explicit transaction grouping. - Evidence:
Endpoints/SuggestEndpoints.cs:103-Endpoints/SuggestEndpoints.cs:109,Endpoints/AdminEndpoints.cs:16-Endpoints/AdminEndpoints.cs:31,Endpoints/AdminEndpoints.cs:220-Endpoints/AdminEndpoints.cs:229. - Recommendation: Wrap multi-entity updates in explicit transactions where consistency matters, or refactor into idempotent command handlers with compensating behavior.
- Acceptance criteria (testable): fault-injection tests prove no partial state after exceptions; transaction boundaries documented per workflow.
- Effort / Risk:
M / Med. - Dependencies (if any): service-layer extraction helpful but not required.
[P1] Strengthen test quality for flaky/time-sensitive cases and security edges
- Problem: Severity
Medium, CategoryTesting. Some tests depend on sleeps and do not cover realistic redirect behavior or overlapping refresh flows. - Evidence:
GameList.Tests/SuggestionTests.cs:379,GameList.Tests/SuggestionTests.cs:575,GameList.Tests/HelperTests.cs:121. - Recommendation: replace
Task.Delayordering checks with deterministic seeded timestamps where feasible; add explicit redirect-follow tests and concurrency-path tests. - Acceptance criteria (testable): no timing sleeps in endpoint tests for ordering; new tests cover redirect-chain rejection and race-sensitive refresh logic.
- Effort / Risk:
M / Low. - Dependencies (if any): P0 redirect-hardening task.
[P2] Externalize i18n/FAQ content from executable JS modules
- Problem: Severity
Low, CategoryComplexity/Documentation. Translation and FAQ payloads are embedded in code, making review and localization hard. - Evidence:
wwwroot/js/i18n.js:1-wwwroot/js/i18n.js:799. - Recommendation: move translation dictionaries and FAQ markdown into versioned JSON/MD assets, load via data module, keep code focused on i18n mechanics.
- Acceptance criteria (testable):
i18n.jscontains behavior only; language assets are schema-validated; app renders identical strings. - Effort / Risk:
M / Low. - Dependencies (if any): frontend module split helps.
[P2] Improve repository-level engineering docs and ownership map
- Problem: Severity
Low, CategoryDocumentation. There is no root README/architecture map/runbook tying module ownership, local setup, and deployment flow together. - Evidence: root README absent; operational docs fragmented across
API.md,SPEC.md,IIS.md,scripts/deploy-ftp.ps1. - Recommendation: add concise
README.mdwith architecture diagram, local run/test commands, operational boundaries, and links to deeper docs. - Acceptance criteria (testable): new contributors can run app + tests using README only; docs align with live auth/deploy behavior.
- Effort / Risk:
S / Low. - Dependencies (if any): P0 auth contract and build hygiene fixes.
D) Quick wins vs strategic refactors
Quick wins (hours to 1 day each):
- Remove stale admin key wording in
API.mdand align with actual auth behavior. - Add
Content Remove="GameList.Tests\**\*"(or equivalent) and verify clean build output. - Add a build check script that fails on warnings and run it locally/CI.
- Completed: removed dead
DeletePlayerRequestfromContracts/Dtos.cs. - Completed: removed unused endpoint handler parameters in
Endpoints/StateEndpoints.cs. - Remove dead UI references (
all-suggestions,nav-vote-next) or add explicit TODO with owner/date. - Replace
Task.Delaytest ordering hacks with deterministic setup in affected tests. - Completed: added module ownership section in
README.md.
Strategic refactors (multi-day/week), staged:
- Phase and policy workflow refactor
- Stage 1: introduce pure phase reader API and separate reconciliation command.
- Stage 2: migrate filters/endpoints to pure reads; remove write side effects from GET.
- Stage 3: enforce via tests that read endpoints do not mutate persistence.
- Backend application service extraction
- Stage 1: extract suggestion create/update/delete logic to one service.
- Stage 2: extract vote/link/unlink workflows with explicit transaction semantics.
- Stage 3: standardize endpoint responses with typed DTOs/ProblemDetails.
- Frontend architecture split
- Stage 1: split
ui.jsinto modal/forms/vote/admin render modules without behavior changes. - Stage 2: remove
window.*bridges and introduce explicit orchestration module. - Stage 3: add lightweight frontend tests for state transitions and API error handling.
- Engineering guardrail rollout
- Stage 1: introduce analyzer/lint config and formatting scripts.
- Stage 2: add CI pipeline gates (build, test, coverage, vulnerability check).
- Stage 3: enable warning budgets and file-size complexity thresholds for hotspot files.
E) Suggested guardrails
Recommended tooling and standards:
- C# static analysis: enable .NET analyzers (
AnalysisLevel latest), nullable warnings strict, and treat warnings as errors in CI. - JS quality: add ESLint + Prettier for
wwwroot/**/*.jsand fail CI on lint errors. - Contract safety: standardize error responses using RFC7807
ProblemDetailsand validate API DTO schema. - CI gates: run
dotnet build,dotnet test GameList.Tests/GameList.Tests.csproj, coverage collection, anddotnet list ... --vulnerable. - Churn guardrails: add a simple check warning when files exceed thresholds (e.g., >500 LOC for JS UI modules, >250 LOC endpoint files).
- Documentation gate: PR checklist requires updates to
API.md/runbooks when auth/endpoints/deploy scripts change. - Security defaults: require known proxy config for forwarded headers and explicit no-redirect HTTP client behavior for external URL validation.
Pragmatic coding standards to prevent backsliding:
- Keep endpoint handlers transport-focused; place business rules in services/validators.
- Keep reads side-effect free; state changes only in explicit commands.
- Avoid duplicate rule definitions across server/client; one source of truth per rule.
- Prefer typed DTOs over anonymous response shapes for non-trivial payloads.