Education / Independent product
Online exams that end on time, even when the network does not.
A multi-tenant exam platform for Indonesian schools and universities: authoring, assignment, timed exam taking, grading, cheating detection and analytics.
- Product
- Ujianku
- Platform
- Web · Mobile
- Built with
- Go, Gin, PostgreSQL, React, TypeScript, Tailwind, Flutter, Docker Compose
- Made at
- Sevtee, as an independent product
Teachers write and assign exams, students take them in the browser with a server-synced clock, and Ujianku Insight shows which questions and which students need attention.
What it does
Ujianku is an online exam platform for Indonesian schools and universities, run as a multi-tenant service. Teachers write multiple-choice and essay exams, assign them to students or whole classes with deadlines and extensions, and grade. Students take exams in the browser with a server-synced clock. Administrators get dashboards, and a module called Ujianku Insight flags questions that are too easy, too hard or possibly broken, and shows class and student risk.
The interface is in Indonesian and English, set per school. A Flutter student app is being built alongside it. The Work case study covers the earlier engineering focus.
What was built
- Authoring with rich text and images, publish and duplicate, and a per-exam security policy; CSV student import; class and teacher management.
- Exam taking with resume, server-synced remaining time, autosave with browser-side recovery, and a server-authoritative auto-submit worker with a three-minute grace window.
- Instant multiple-choice grading, an essay grading queue with drafts and batch grading, and results export to CSV and PDF.
- Cheating detection: tab switches, copy and paste, context menu and developer-tools attempts logged per attempt, with per-exam thresholds and actions, a device policy, and live monitoring by polling.
- Insight analytics computed on read from plain SQL.
- School registration with email verification, roles, a platform operator panel with suspend and impersonation, manual subscription plans, an append-only audit trail, a public API with signed webhooks, bot protection and operator alerts.
- A Flutter student app, first milestone: login, exam list, runner, resume and a local SQLite answer queue, covered by 39 golden-test states.
Engineering decisions
- Insight metrics are computed on read with plain SQL aggregates, not materialized views or snapshot tables, so the exam hot path never writes analytics and nothing can drift. A local load test measured a p95 of 20 ms or less at 50 virtual users.
- Webhooks are delivered by a database-polling worker with retries rather than Redis, which was not provisioned and would still have needed a database sweep as the safety net.
- Rate limiting is in memory and valid for a single backend replica. A shared limiter was deliberately deferred.
- The interface language is a school-level setting forced from the server, so a student cannot change what their school chose.
- Every tenant-owned query is filtered by school, and non-owner teachers get 404 rather than 403, so resources do not leak their existence.
What I learned
Answers are the whole product, and school networks fail. Saving is a direct API call debounced at about two seconds. Anything that fails waits in browser storage keyed by attempt and is flushed when the page reloads or the browser comes back online. The mobile app keeps the same promise with a SQLite table and a per-row revision, so a late success can never erase a newer edit.
The second lesson was that time must belong to the server. The submission deadline is stored once when an attempt starts, recomputed if a teacher changes the assignment, accepted three minutes late, and then enforced by a worker whether or not the client is still there.
Retrofitting two languages across about 119 hardcoded files taught me to decide the language model early. Building the Flutter client with golden tests caught invisible dialog text and overflow at large font sizes before any device did. And recording tutorial videos of the product surfaced a list of bugs, including an authorization gap between teachers of the same school, which made recording your own product a form of testing.