During the development of an exam platform for a technical university, we faced requirements: 10,000 students, 200 subjects, 50 question types, peak load of 500 simultaneous sessions. The system must guarantee data integrity during failures and prevent cheating. The cost of error is not just a grade—it's the institution's reputation or certification of a specialist. Here's how we solve these challenges in practice.
We design a modular architecture where each exam type is isolated in its own service: auto-graded tests, manually reviewed assignments, adaptive (CAT) tests, and proctored exams. This approach simplifies scaling and adding new types. For example, for one client we implemented support for 12 programming languages in coding tasks, each in a Docker container with isolation.
Exam types and their components
| Type |
Features |
Questions per typical test |
| Auto-graded |
Tests, multiple choice, short answers |
30–60 |
| Manual review |
Essays, coding tasks, case studies |
3–10 |
| Adaptive (CAT) |
Difficulty adjusts to answers |
15–25 (30% fewer than linear) |
| Proctored |
With observer (online or offline) |
any |
| Open-book |
Materials allowed |
any |
Each type requires its own components: for essays—an editor with plagiarism check, for coding tasks—code execution in an isolated container. The question bank ranges from 500 to 5,000 items.
How is cheating prevented?
Kiosk mode blocks 99% of tab-switching attempts. Implementation via document.onfullscreenchange + detecting visibilitychange:
document.addEventListener('visibilitychange', () => {
if (document.hidden && examInProgress) {
recordViolation('tab_switch', { timestamp: Date.now() });
}
});
Randomization: question order and answer options are shuffled for each examinee—shuffle_seed is generated at start and persisted. Probability of two students getting identical variants is less than 1 in a million. Time limits are stored server-side, client syncs every 30 seconds. On timeout—automatic submission. This set of measures reduces violations by 80% compared to a simple timer.
How does adaptive testing work?
Computer Adaptive Testing (CAT) selects questions based on estimated ability. The algorithm uses Item Response Theory (IRT), according to Item Response Theory (Wikipedia): each question in the bank has parameters for difficulty (b), discrimination (a), and guessing probability (c). We implement the 3PL model.
Process:
- Start with a medium question (θ = 0).
- Correct answer → next question harder.
- Incorrect → easier.
- Ability estimate (θ) recalculated after each answer.
We use the catirt library (R) or a custom implementation. The result is an accurate assessment in 15–25 questions instead of 40–60 for a linear test, reducing time by 40%.
Example of estimating question parameters via IRT
For each question, the three-parameter logistic function is computed: P(θ) = c + (1-c) / (1 + exp(-1.7*a*(θ-b))). Parameters are estimated using maximum likelihood on a calibration sample (at least 200 responses per question).
Reliability and recovery
Exams must not be lost. Auto-saving current answers every 30 seconds (POST to server). On connection loss—resume after reconnection from the last saved state. In practice, 99.9% of sessions finish without data loss. For technical issues—a procedure for reopening exam sessions. Answers are stored in exam_answers with submitted_at, separate from the final score.
Online proctoring
Two levels:
| Level |
Technology |
Detection accuracy |
Relative cost |
| AI proctoring |
MediaPipe/OpenCV self-hosted |
70% |
1x |
| Live proctoring |
WebRTC |
95% |
3x |
| Hybrid (AI + live) |
Combination |
95% |
1.5x |
A self-hosted solution using MediaPipe/OpenCV reduces costs by 3x compared to commercial services. Webcam data is recorded and analyzed post-factum, reducing server load during the exam.
What’s included in the result
The basic package includes: question bank, auto-grading system, anti-cheat module, certificate generation, and deployment documentation plus API docs. The extended package additionally includes adaptive testing (CAT), proctoring, LMS integration, custom question types, and training for up to 5 staff. We always provide full source code with no restrictions and a 3-month warranty.
Development stages
- Analytics: gather requirements, load profiling, architecture design.
- Design: UI prototype, data model, API specification.
- Implementation: iterative development with demos every 2 weeks.
- Testing: load testing up to 1,000 simultaneous sessions, security audit.
- Deployment: deploy on your infrastructure and hand over documentation.
Timelines and scope of work
| Package |
Timeline |
Scope |
| Basic |
3–4 months |
Question bank, timer, auto-grading, anti-cheat, certificates |
| Extended |
5–8 months |
Everything in basic + proctoring, CAT, essay review, integrations |
The cost includes: documentation, source code, training for up to 5 staff, 3-month warranty. Specific pricing is determined individually after requirements analysis.
Our expertise
5+ years of experience in developing high-load educational platforms. 30+ projects for universities and online schools with audiences from 500 to 50,000 users. Stack: React/Next.js, Node.js/Nest.js, PostgreSQL, Redis, Docker, Kubernetes. We’ll assess your project for free—contact us for a consultation and receive demo access to a prototype.
What Does SaaS Platform Development Involve? Multi-Tenancy, Billing, and Beyond
We know this pain by heart. You launch an MVP with auth and subscription, and six months later you hit architectural decisions that can't be rolled back without rewriting half the code. Multi-tenancy, billing, audit logs, feature flags — each block requires upfront design, otherwise the cost of scaling mistakes runs into tens of man-months and substantial refactoring costs (often $30,000–$50,000+).
Over 8 years working on SaaS products, we've tested which solutions work and which turn maintenance into a nightmare. Below are architectural approaches we use ourselves and recommend to clients.
How we build multi-tenancy: isolation without overhead
The first decision is the data separation scheme. Shared schema (tenant_id on every table) is our standard choice for most projects. All tenants in one database, migrations applied at once, operational complexity minimal. In Laravel we implement it via Global Scope:
protected static function booted(): void
{
static::addGlobalScope('tenant', function (Builder $builder) {
$builder->where('tenant_id', TenantContext::current()->id);
});
}
The global scope is only the first line of defense. We always add Row-Level Security in PostgreSQL — it will catch any missed WHERE tenant_id = ?:
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::uuid);
For enterprise clients requiring physical isolation, we allocate a separate database. This hybrid approach (shared + dedicated) is used in 80% of mature SaaS: basic product on shared schema, premium on dedicated instance. We implement it from the first sprint to avoid rewriting logic later. Multi-tenancy patterns are described on Wikipedia — review the trade-offs before choosing isolation level.
Why Is Billing the Most Underestimated Block?
Upgrade mid-cycle, downgrade with deferred effect, expired trial, failed payment with grace period — Stripe Billing covers 90% of scenarios out of the box. We always process webhooks (customer.subscription.updated, invoice.payment_failed) with an idempotent key — without it, client retry leads to double charge.
For CIS markets — YooKassa or Tinkoff recurring. Their APIs are less convenient but cover 54-FZ requirements.
Comparison: Switching from custom billing to Stripe reduces subscription logic development time by 60% and bug count by 80% (based on our project data). That translates to $15,000–$25,000 savings on a typical SaaS MVP.
Onboarding: how not to lose the user before aha-moment
Technically, onboarding is a wizard with persistent state that cannot be accidentally skipped. Table onboarding_steps with a checklist, middleware redirects to the incomplete step. After completion — a flag in user settings, middleware disabled.
Critical nuance: show real product progress, not abstract steps. "Create your first report" instead of "Complete step 3 of 5." We use drip campaigns via Customer.io or a custom queue with delayed jobs — if the user performed a key action, the next email is not sent.
How to Implement Feature Flags and Access Control?
SaaS with plans requires granular control. Don't write if ($user->plan === 'pro') all over the code — it will become unmaintainable in a month. Instead:
- Backend: Gate + Policy with checks via
features table linked to plans.
- Frontend: context with flags loaded at app initialization.
- Open-source tools: Unleash or Growthbook — UI for A/B testing and rollout.
Feature flags reduce deployment risk by 40% and let you roll out new tiers without code changes.
How to Protect API from Aggressive Clients?
Rate limiting is a must for public API. One client can bring down all others. In Laravel we use Redis with sliding window counter:
| Plan |
Limit |
Response Headers |
| Free |
100 req/h |
X-RateLimit-Limit: 100 |
| Pro |
1 000 req/h |
X-RateLimit-Limit: 1000 |
| Enterprise |
10 000 req/h |
X-RateLimit-Limit: 10000 |
Each response contains X-RateLimit-Remaining and X-RateLimit-Reset — clients rely on these headers. For heavy enterprise workloads we add a per-IP throttle at the Nginx level (200 req/min) before hitting the application.
Audit Logs and Monitoring: What, Who, and When?
Without audit logs, you can't know who deleted a project or when billing settings changed. Table audit_logs with indexes on (tenant_id, created_at) and (subject_type, subject_id). In Laravel — Observers on key models.
Example Observer implementation for Model
class OrderObserver
{
public function created(Order $order): void
{
AuditLog::create([
'tenant_id' => $order->tenant_id,
'user_id' => auth()->id(),
'action' => 'created',
'subject_type' => Order::class,
'subject_id' => $order->id,
]);
}
}
Monitoring: Sentry for exception tracking, Grafana + Prometheus for metrics. Alerts on error rate > 5% and response time p95 > 2s. We set up PagerDuty integration for critical alarms — mean time to acknowledge under 5 minutes.
Our Team's Experience and Guarantees
Our engineers have 8+ years of experience with SaaS platforms, 50+ projects from startups to enterprise with millions of loads. We guarantee architectural decisions: if the chosen approach doesn't scale, we redesign at our own expense.
Deliverables and Guarantees
- Architecture documentation: diagrams, ERD, sequence diagrams.
- CI/CD setup (GitHub Actions / GitLab CI).
- Access to repository, staging, and production.
- Team training: 2–3 sessions on code review and runbook.
- Post-launch support for 1 month.
- Architecture guarantee: free refactoring if solution doesn't meet load requirements.
Work Process
- Discovery (1–2 weeks) — audit current architecture, MVP scope, feature priorities.
- Design (1 week) — stack selection, multi-tenancy scheme, billing plan.
- Development (4–12 weeks) — 2-week sprints, demo after each.
- Testing (1 week) — load tests under target load, security audit.
- Deployment and training (1 week) — rollout, monitoring setup, documentation handover.
Timeline Estimates
| Stage |
Duration |
| MVP (core features + auth + billing) |
12–16 weeks |
| Full product with admin panel |
20–28 weeks |
| Enterprise SaaS with multi-tenancy + audit |
28–40 weeks |
Pricing is calculated individually — contact us for a project estimate within 2 days. Order turnkey development: from design to deployment with architecture guarantee. Get a consultation on your product architecture — first hour free.