I Didn't Plan to Build an Offline-First Attendance System. Firestore Forced Me To.
DEV Community

I Didn't Plan to Build an Offline-First Attendance System. Firestore Forced Me To.

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." Like most student developers, I focused on getting the features working first.

The application already had everything I needed for the MVP:

  • Dynamic QR-based attendance
  • Firebase Authentication
  • Firestore database
  • Anti-proxy validation
  • Admin dashboard
  • Meeting management

My daily workflow looked something like this:

Code โ†“ Run โ†“ Test โ†“ Scan QR โ†“ Debug โ†“ Repeat

Everything seemed to be progressing smoothly. What I wasn't paying attention to was something happening quietly in the background. Every refresh. Every API call. Every test. Every debugging session. All of them were consuming Firestore document reads.

The Problem I Never Expected

One day, while testing the application, things suddenly stopped working. Attendance requests started failing. Firestore wasn't responding. Like most developers, my first thought was: "I definitely broke something."

I checked my recent commits. I reviewed the API routes. I verified Firebase Authentication. I even looked through my Firestore Security Rules. Everything looked perfectly fine.

Then I opened the Firebase Console. That's when I saw it.

Firestore Reads: 50,000 / 50,000

I had exhausted the entire free-tier quota. Not because thousands of students were using my application. Not because it had gone viral. Simply because I had spent days developing and debugging it. Ironically, I had become my own biggest user.

The Easy Fix Wasn't the Right Fix

Technically, I could have waited for the daily quota to reset. Problem solved. But this wasn't just another side project. It was an attendance system intended to be used during actual UBA meetings. If Firestore became unavailable during a meeting, attendance couldn't simply stop.

So instead of asking, "How do I reduce Firestore reads?" I asked a different question. "How do I make the system continue working even when Firestore isn't available?" That single question completely changed the architecture of the project.

Offline-First Was Never the Plan

Before this happened, offline support wasn't even on my roadmap. Every attendance request depended on Firestore. Every scan required the backend. Which meant one dependency controlled whether the entire application worked. I didn't like that.

So I redesigned the workflow. Instead of immediately writing attendance to Firestore, the application could now continue functioning locally whenever the backend wasn't available. Attendance records would be stored securely on the device and synchronised automatically once connectivity or database access returned.

The project unexpectedly became offline-first.

But There Was One Catch...

Building offline support for a normal CRUD application is relatively straightforward. Building it for an anti-proxy attendance system isn't. Offline mode couldn't become an easy way to bypass the security model. The system still had to guarantee:

  • Attendance integrity
  • Duplicate prevention
  • Secure local storage
  • Reliable synchronisation
  • Consistent validation after syncing

The goal wasn't simply to make the application work offline. It was to ensure the same anti-proxy workflow remained intact whether the application was online or offline. That made the redesign much more interesting than simply adding a cache.

Solving the Symptom vs Solving the Cause

The offline architecture solved the immediate problem. But after everything was working again, I realised something. Running out of Firestore reads wasn't the real problem. My application was. I had built the MVP quickly. I hadn't built it efficiently.

That led me to review every interaction between the application and Firestore. Questions I had never asked before suddenly became important.

  • Do I really need this document read?
  • Why am I fetching the same document multiple times?
  • Can this data be cached?
  • Can I combine multiple reads into one request?
  • Is every database write actually necessary?

Those questions ended up improving the architecture far more than any new feature.

Optimising Firestore Usage

Once the offline system was stable, I shifted my attention to optimisation.

  • I reduced unnecessary Firestore reads.
  • I optimised database queries.
  • I removed redundant writes wherever possible.
  • I introduced Render's free cache to store frequently accessed data, reducing repeated requests for information that rarely changed.

These weren't flashy improvements. Users would never notice them directly. But together, they made the application significantly more efficient and much less dependent on Firestore's free-tier limits.

What I Learned

When I started this project, I thought software engineering was mostly about building features. This experience taught me something different. Sometimes the most important engineering decisions don't happen while adding new features. They happen when your assumptions break.

Running out of Firestore reads forced me to think beyond "making it work." It pushed me to think about resilience. Reliability. Efficiency. And designing systems that continue working even when one of their dependencies doesn't.

Looking back, hitting Firestore's limit was frustrating. Today, I'm actually grateful it happened. Without that unexpected limitation, I probably would never have redesigned the application into something far more resilient than the MVP I originally planned.

Final Thoughts

This project began as a simple anti-proxy attendance system for my university. It unexpectedly became a lesson in building software that can survive constraints. Sometimes architecture evolves because of careful planning. Sometimes it evolves because reality forces you to rethink everything.

For me, it took exactly 50,000 Firestore document reads to learn that lesson. And honestly, I wouldn't build it any other way now.

Thanks for Reading! ๐Ÿš€

Have you ever hit a cloud free-tier limit, API quota, or infrastructure constraint that completely changed the way you designed your application? I'd love to hear your experience in the comments. Some of the best engineering lessons come from the problems we never planned for.

Comments

No comments yet. Start the discussion.