Mayowa.AAvailable
Back to blog
Build Log

Departmental Election Platform

August 2026 · 8 min

1 read

When I started building the Departmental Election Platform, the requirements seemed straightforward: let 250+ students vote for their preferred candidates across multiple positions. In practice, that meant building a system that could handle concurrent voting, prevent double-voting, provide real-time results, and maintain complete transparency — all while being accessible to students across four different levels.

The brief from the department was clear: "We need an election system that students can trust." But building trust in a voting system requires more than just functional code. It requires security, transparency, and the ability to verify that everything happened fairly.

## The Architecture Decision

I initially considered using Supabase, but after encountering permission and authentication hurdles, I migrated to Firebase. The decision wasn't just about preference — it was about reliability. Firebase's authentication system handled the 250+ concurrent users without breaking a sweat, and Firestore's real-time capabilities meant students could see results update as votes came in.

The database structure evolved into three main collections:

```javascript

// Students — preloaded from CSV before elections

{

matric_number: "2025/MTBM/HND/317",

full_name: "Abraham Mayowa",

level: "HND2",

registered_status: false,

voting_status: false

}

// Elections — with draft → open → closed workflow

{

title: "Departmental Election 2025",

status: "open", // draft | open | closed

positions: ["President", "Vice President", "Secretary"],

candidates: [...] // nested for quick retrieval

}

// Votes — permanently locked once cast

{

electionId: "...",

positionId: "...",

candidateId: "...",

studentId: "...",

createdAt: timestamp,

voteHash: "unique-identifier" // for verification

}

```

The key insight? Every vote needed to be immutable. Once a student clicks "submit," there's no undo — not for them, not for me, not even for an admin. This required careful implementation of database rules and transaction isolation.

## The Security Problem

One of the biggest challenges was ensuring that only eligible students could vote, and only once per position. The solution involved:

1. **Role-based access control**: Three tiers — Student, Admin, and Super Admin

2. **Device fingerprinting**: Each phone/computer got linked to one student account

3. **Rate limiting**: 5 failed login attempts triggered a temporary lockout

4. **Vote verification**: Every vote received a unique hash that students could use to verify their vote was recorded

The most unexpected challenge came from the authentication flow. Initially, I used Supabase's built-in authentication, but I kept hitting rate limits and permission issues. Moving to Firebase solved this, but required rethinking the entire user management flow.

## The Downtime Incident

About 2 hours into the live election, the system went down.

The issue wasn't the code — it was the database indexes. Firestore requires explicit indexes for complex queries, and while I'd created indexes for the main queries, I'd missed one for the "check if student has already voted" query. When hundreds of students started voting simultaneously, the query started timing out, and the system became unresponsive.

The fix took about 45 minutes: I identified the missing index, added it through the Firebase console, and the system recovered. But those 45 minutes felt like an eternity, knowing that students were waiting to vote.

## What I Learned

### 1. Index Everything (or Suffer Later)

The downtime was entirely avoidable. A proper indexing strategy is non-negotiable for any system handling concurrent operations. I now treat indexes as part of the design phase, not an afterthought.

```javascript

// Every query needs an index. EVERY query.

// Example: students voting status query

await db.collection('votes')

.where('electionId', '==', electionId)

.where('studentId', '==', studentId)

.get();

// Requires composite index on (electionId, studentId)

```

### 2. Test Under Realistic Load

I'd tested the system with 10 users, but 250 users changed everything. Database performance, authentication rate limits, and even the Cloudinary upload limits became bottlenecks I hadn't anticipated. Next time, I'll use load testing tools to simulate real traffic patterns.

### 3. Communication During Outages

During the downtime, I was panicking — but what mattered more was communicating with the department. A clear message: "We're experiencing a technical issue, working to resolve it, will update you in 15 minutes" would have been better than silence. I learned the hard way that transparency matters as much as uptime.

### 4. The Voting Experience Must Be Flawless

Students were emotionally invested in this election. Any glitch — a slow page load, a confusing error message — caused anxiety. I spent most of my time on the "error states": what happens if a student accidentally tries to vote twice, or if their internet drops mid-vote.

### 5. Verification Builds Trust

The most popular feature wasn't the voting — it was the verification page. Students could check their own vote's hash and verify it was recorded correctly. This transparency turned skeptics into advocates.

## Results

The election processed **250+ votes** across 7 positions, with **100% participation** from eligible students. The system handled the load with minimal issues (besides the indexing incident), and results were available instantly after the election closed.

What I'm most proud of? The integrity checks. Every vote is verifiable, every action is logged, and the results are auditable. The department can confidently say this was the most transparent election they've ever had.

## Tech Stack Used

- **Frontend**: React.js + Tailwind CSS v4 (CSS-first approach, no config file needed)

- **Backend**: Firebase (Authentication, Firestore Database)

- **Image Upload**: Cloudinary (avoided Firebase Storage CORS issues)

- **Real-time**: Firestore real-time listeners for live results

- **Deployment**: Vercel

## What's Next

- More robust indexing strategy

- Load testing integration

- Automated rollback procedures

- Better monitoring and alerting

---

*This was my first time building a production-scale voting system. It wasn't perfect, but it was an honest effort, and I learned far more from the mistakes than I did from the things that worked.*

*The system successfully ran a departmental election with 250+ students without any lost votes or security breaches. That's a win in my book.*

Enjoyed this post?

Comments

No account needed — leave a name or comment anonymously.

Loading comments…