How an unexpected database limit led me to build an offline-first anti-proxy attendance system.
Introduction
When I started building an attendance system for my university's UBA (Unnat Bharat Abhiyan) club, I had one clear instruction from our coordinator:
"Build an MVP as soon as possible."
So I did what most student developers would do. I focused on shipping features instead of watching dashboards.
Every new feature meant refreshing pages, testing authentication, creating meetings, scanning QR codes, fixing bugs, and repeating the same workflow again and again.
I was so focused on making the system work that I never noticed something else happening in the background.
Every debug session was consuming Firestore reads.
The Day Everything Stopped Working
After several hours of development, it was finally time to demonstrate the MVP.
Everything looked ready.
The QR authentication worked.
Meetings could be created.
Students could scan codes.
Then the application suddenly stopped communicating with Firestore.
At first, I thought I had introduced a bug.
I checked my code.
Nothing seemed wrong.
A few minutes later, I found the real culprit.
I had unknowingly exhausted Firestore's free-tier quota of 50,000 document reads.
Not because of production traffic.
Not because thousands of students were using it.
Simply because I had been debugging the application so much.
That moment completely changed how I thought about the project.
Waiting Wasn't an Option
The easiest solution would have been to wait for the quota to reset.
But this wasn't just another side project.
The attendance system had to work during meetings.
If the database became unavailable, attendance couldn't simply stop.
So instead of asking:
"How do I reduce Firestore reads?"
I asked a different question.
"How can the application continue working even if Firestore becomes unavailable?"
That single question changed the architecture of the project.
Building an Offline-First Attendance System
The obvious answer was offline support.
But attendance systems aren't ordinary CRUD applications.
They also have to prevent proxy attendance.
Moving offline couldn't mean compromising security.
So the offline mode had to preserve the same guarantees as the online system.
The application needed to:
Continue recording attendance without Firestore.
Prevent duplicate submissions.
Maintain anti-proxy validation.
Store attendance securely until synchronization.
Upload records once connectivity or database access returned.
Instead of treating offline mode as a backup feature, it became a core part of the system's architecture.
Solving One Problem Revealed Another
The offline architecture solved the immediate issue.
But I still wasn't happy.
The original problem hadn't really been "no internet."
It was inefficient database usage.
So I started reviewing every interaction with Firestore.
Questions I hadn't asked before suddenly became important.
Do I really need this read?
Can this data be cached?
Can multiple requests be combined?
Why am I fetching the same document repeatedly?
I realized that improving resilience wasn't enough.
The system also needed to become more efficient.
Reducing Firestore Reads
Once the MVP was stable again, I began optimizing the backend.
Some of the improvements included:
Reducing unnecessary document reads.
Avoiding repeated requests for unchanged data.
Caching frequently accessed information using Render's free cache.
Optimizing database queries.
Reducing redundant writes wherever possible.
None of these optimizations changed the features users saw.
But together, they significantly reduced the load on Firestore and made the application far more sustainable on the free tier.
What This Experience Taught Me
Looking back, exhausting Firestore's free-tier quota was frustrating.
But it also forced me to think like a software engineer rather than just a programmer.
I stopped asking:
"How do I make this feature work?"
and started asking:
"How do I make this system keep working when one of its dependencies fails?"
That shift in mindset led to a better architecture than the one I originally planned.
Sometimes the best engineering decisions aren't made during planning.
They're made when reality breaks your assumptions.
Final Thoughts
The attendance system I set out to build was meant to prevent proxy attendance.
The system I ended up building also learned how to survive database limits, continue operating offline, and make smarter use of backend resources.
Running out of Firestore reads became one of the most valuable debugging sessions of the entire project.
💬 I'd love to hear your thoughts.
Have you ever hit a free-tier limit or an unexpected infrastructure constraint that completely changed your system design? Share your experience in the comments.