<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: siddarthpatelkama </title>
    <description>The latest articles on DEV Community by siddarthpatelkama  (@siddarthpatelkama).</description>
    <link>https://dev.to/siddarthpatelkama</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4003834%2F5b7c10be-984c-41f4-9e6e-5705458fce67.jpg</url>
      <title>DEV Community: siddarthpatelkama </title>
      <link>https://dev.to/siddarthpatelkama</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siddarthpatelkama"/>
    <language>en</language>
    <item>
      <title>What Exhausting 50,000 Firestore Reads Taught Me About Database Design</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Wed, 02 Sep 2026 04:56:03 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/what-exhausting-50000-firestore-reads-taught-me-about-database-design-4hk</link>
      <guid>https://dev.to/siddarthpatelkama/what-exhausting-50000-firestore-reads-taught-me-about-database-design-4hk</guid>
      <description>&lt;p&gt;How I redesigned the data layer of my anti-proxy attendance system around access patterns, security, offline synchronization, and efficient Firestore usage.&lt;/p&gt;

&lt;p&gt;When I first built my attendance system, I wasn't thinking much about database architecture.&lt;/p&gt;

&lt;p&gt;I had a working MVP.&lt;/p&gt;

&lt;p&gt;Students could log in.&lt;br&gt;
Teachers could create attendance sessions.&lt;br&gt;
Students could scan dynamic QR codes.&lt;br&gt;
Attendance records were stored in Firestore.&lt;/p&gt;

&lt;p&gt;It worked.&lt;/p&gt;

&lt;p&gt;Until I started using it heavily.&lt;/p&gt;

&lt;p&gt;Then I learned a lesson that only becomes obvious after something breaks:&lt;/p&gt;

&lt;p&gt;Your database structure isn't just about how you store data. It's about how your application needs to retrieve it.&lt;/p&gt;

&lt;p&gt;And in my case, that lesson started with 50,000 Firestore reads.&lt;/p&gt;

&lt;p&gt;The Problem Wasn't Just the Quota&lt;/p&gt;

&lt;p&gt;In an earlier part of this series, I wrote about how I unexpectedly exhausted my Firestore free-tier reads while developing and testing the system.&lt;/p&gt;

&lt;p&gt;That incident pushed me to build offline capabilities and start reducing unnecessary database operations.&lt;/p&gt;

&lt;p&gt;But it also exposed a deeper problem.&lt;/p&gt;

&lt;p&gt;I had been thinking about Firestore mainly as:&lt;/p&gt;

&lt;p&gt;"Where should I store this data?"&lt;/p&gt;

&lt;p&gt;I needed to start thinking:&lt;/p&gt;

&lt;p&gt;"What data will my application need to retrieve, how often, and in what combination?"&lt;/p&gt;

&lt;p&gt;That shift changed how I looked at the entire data layer.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I Stopped Thinking in Tables&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Coming from SQL-style database thinking, my first instinct was to model everything around entities.&lt;/p&gt;

&lt;p&gt;Students.&lt;/p&gt;

&lt;p&gt;coordinaters.&lt;/p&gt;

&lt;p&gt;Attendance.&lt;/p&gt;

&lt;p&gt;meetings.&lt;/p&gt;

&lt;p&gt;Sessions.&lt;/p&gt;

&lt;p&gt;That isn't necessarily wrong.&lt;/p&gt;

&lt;p&gt;But Firestore isn't a relational database with tables and joins.&lt;/p&gt;

&lt;p&gt;It is a document-oriented database built around collections and documents, with support for nested structures and subcollections.&lt;/p&gt;

&lt;p&gt;So instead of asking:&lt;/p&gt;

&lt;p&gt;"What are all my entities?"&lt;/p&gt;

&lt;p&gt;I started asking:&lt;/p&gt;

&lt;p&gt;"What does the application actually need to read?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj0m561h31trmks53gstz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj0m561h31trmks53gstz.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fber9uyi4rkwrlfic12t5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fber9uyi4rkwrlfic12t5.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That sounds like a small difference.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I Started With Access Patterns&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider what happens when a student tries to mark attendance.&lt;/p&gt;

&lt;p&gt;The system needs to establish several things:&lt;/p&gt;

&lt;p&gt;Who is this user?&lt;br&gt;
Is the authentication valid?&lt;br&gt;
Is the dynamic QR valid?&lt;br&gt;
Is this device associated with the account?&lt;br&gt;
Is there an active attendance session?&lt;br&gt;
Has this student already marked attendance?&lt;br&gt;
Should an attendance record be created?&lt;/p&gt;

&lt;p&gt;The important thing here is that these aren't independent database operations.&lt;/p&gt;

&lt;p&gt;They're part of one application workflow.&lt;/p&gt;

&lt;p&gt;That meant the database structure had to support the workflow rather than forcing the application to repeatedly search for unrelated pieces of information.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Attendance Isn't Just a Property of a Student&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the most important modeling decisions was treating attendance as its own record.&lt;/p&gt;

&lt;p&gt;A student isn't simply:&lt;/p&gt;

&lt;p&gt;Student&lt;br&gt;
    attendance = 87%&lt;/p&gt;

&lt;p&gt;That number doesn't tell me much.&lt;/p&gt;

&lt;p&gt;I need to know:&lt;/p&gt;

&lt;p&gt;Which meeting?&lt;br&gt;
Which session?&lt;br&gt;
Which date?&lt;br&gt;
Was the attendance marked successfully?&lt;br&gt;
What authentication flow was used?&lt;br&gt;
Was the request associated with the expected device?&lt;br&gt;
When was it recorded?&lt;/p&gt;

&lt;p&gt;Attendance is really an event.&lt;/p&gt;

&lt;p&gt;And events deserve their own records.&lt;/p&gt;

&lt;p&gt;This also gives the application something much more useful to work with.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;p&gt;"What is this student's attendance?"&lt;/p&gt;

&lt;p&gt;the system can ask:&lt;/p&gt;

&lt;p&gt;"What attendance events belong to this student?"&lt;/p&gt;

&lt;p&gt;That distinction becomes increasingly important as the application grows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Database Became Part of the Security Pipeline&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was another thing I didn't appreciate when I started.&lt;/p&gt;

&lt;p&gt;My dynamic QR system already had multiple layers of verification.&lt;/p&gt;

&lt;p&gt;But authentication doesn't end when the QR is scanned.&lt;/p&gt;

&lt;p&gt;The request still needs to move through the backend before attendance is accepted.&lt;/p&gt;

&lt;p&gt;The general flow became:&lt;/p&gt;

&lt;p&gt;Student&lt;br&gt;
   ↓&lt;br&gt;
User Authentication&lt;br&gt;
   ↓&lt;br&gt;
Dynamic QR Validation&lt;br&gt;
   ↓&lt;br&gt;
Device Verification&lt;br&gt;
   ↓&lt;br&gt;
Backend Validation&lt;br&gt;
   ↓&lt;br&gt;
Firestore&lt;br&gt;
   ↓&lt;br&gt;
Attendance Record&lt;/p&gt;

&lt;p&gt;The database isn't responsible for deciding whether a student is trustworthy.&lt;/p&gt;

&lt;p&gt;But it stores the state that the backend needs to make that decision.&lt;/p&gt;

&lt;p&gt;That makes the data model part of the security architecture.&lt;/p&gt;

&lt;p&gt;And this is one reason I became much more careful about where authentication-related state lived.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I Learned Not to Read Everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sounds obvious.&lt;/p&gt;

&lt;p&gt;But it's surprisingly easy to accidentally build an application that retrieves far more data than it needs.&lt;/p&gt;

&lt;p&gt;For example, imagine a dashboard that only needs the current attendance session.&lt;/p&gt;

&lt;p&gt;There is no reason for the application to retrieve an entire history of attendance records just to determine what is happening right now.&lt;/p&gt;

&lt;p&gt;The same principle applies everywhere:&lt;/p&gt;

&lt;p&gt;Don't ask:&lt;br&gt;
"How much data can I retrieve?"&lt;/p&gt;

&lt;p&gt;Ask:&lt;br&gt;
"What's the smallest amount of data this operation needs?"&lt;/p&gt;

&lt;p&gt;Firestore supports document-level queries, filtering, sorting, limits, and pagination mechanisms, which makes this style of access-pattern-driven design possible.&lt;/p&gt;

&lt;p&gt;This became particularly important after my 50,000-read incident.&lt;/p&gt;

&lt;p&gt;Every unnecessary read stopped feeling harmless.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Offline Support Changed the Data Flow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then there was another complication.&lt;/p&gt;

&lt;p&gt;The attendance system couldn't assume that the network would always be available.&lt;/p&gt;

&lt;p&gt;That meant the architecture eventually needed to support a flow like:&lt;/p&gt;

&lt;p&gt;User Action&lt;br&gt;
    ↓&lt;br&gt;
Local State&lt;br&gt;
    ↓&lt;br&gt;
Pending Attendance&lt;br&gt;
    ↓&lt;br&gt;
Network Available?&lt;br&gt;
   ↙        ↘&lt;br&gt;
 YES         NO&lt;br&gt;
 ↓            ↓&lt;br&gt;
Firestore   Keep Locally&lt;br&gt;
              ↓&lt;br&gt;
          Sync Later&lt;/p&gt;

&lt;p&gt;Now the database wasn't simply receiving requests from the frontend.&lt;/p&gt;

&lt;p&gt;There was a synchronization problem too.&lt;/p&gt;

&lt;p&gt;An attendance action could exist locally before it existed remotely.&lt;/p&gt;

&lt;p&gt;That forced me to think about things like:&lt;/p&gt;

&lt;p&gt;pending records&lt;br&gt;
duplicate submissions&lt;br&gt;
synchronization&lt;br&gt;
conflict handling&lt;br&gt;
what happens when the network comes back&lt;br&gt;
how the backend should validate synced data&lt;/p&gt;

&lt;p&gt;The database architecture therefore had to coexist with the offline architecture.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Security and Performance Pulled in Different Directions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was probably one of the more interesting tradeoffs.&lt;/p&gt;

&lt;p&gt;Security wants more validation.&lt;/p&gt;

&lt;p&gt;Performance wants fewer operations.&lt;/p&gt;

&lt;p&gt;Offline support wants local state.&lt;/p&gt;

&lt;p&gt;Consistency wants reliable synchronization.&lt;/p&gt;

&lt;p&gt;And Firestore usage needs to remain efficient.&lt;/p&gt;

&lt;p&gt;You can't optimize one dimension without considering the others.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;More validation&lt;br&gt;
      ↓&lt;br&gt;
More reads / writes&lt;br&gt;
      ↓&lt;br&gt;
Potentially higher latency&lt;/p&gt;

&lt;p&gt;Less validation&lt;br&gt;
      ↓&lt;br&gt;
Fewer operations&lt;br&gt;
      ↓&lt;br&gt;
Potentially weaker verification&lt;/p&gt;

&lt;p&gt;So the goal wasn't:&lt;/p&gt;

&lt;p&gt;"Use the fewest Firestore operations possible."&lt;/p&gt;

&lt;p&gt;The goal was:&lt;/p&gt;

&lt;p&gt;"Use the database operations that actually contribute to the correctness and security of the workflow."&lt;/p&gt;

&lt;p&gt;That's a much better optimization target.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The 50,000 Reads Incident Changed How I Debug&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before the quota incident, I mostly looked at whether the feature worked.&lt;/p&gt;

&lt;p&gt;After it, I started asking different questions.&lt;/p&gt;

&lt;p&gt;When something happened in the UI:&lt;/p&gt;

&lt;p&gt;What triggered this read?&lt;/p&gt;

&lt;p&gt;Why was this document needed?&lt;/p&gt;

&lt;p&gt;Could this have been cached?&lt;/p&gt;

&lt;p&gt;Did the application already have this information?&lt;/p&gt;

&lt;p&gt;Could I retrieve a smaller result?&lt;/p&gt;

&lt;p&gt;Is this happening once or repeatedly?&lt;/p&gt;

&lt;p&gt;That mindset is useful beyond Firestore.&lt;/p&gt;

&lt;p&gt;A slow application isn't always slow because the database itself is slow.&lt;/p&gt;

&lt;p&gt;Sometimes the application is simply asking the database the same question over and over.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I Started Treating the Data Layer as Architecture&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This became the biggest lesson from the whole process.&lt;/p&gt;

&lt;p&gt;Initially, my architecture looked conceptually like:&lt;/p&gt;

&lt;p&gt;Frontend&lt;br&gt;
   ↓&lt;br&gt;
Backend&lt;br&gt;
   ↓&lt;br&gt;
Firestore&lt;/p&gt;

&lt;p&gt;But the real system became closer to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                ┌──────────────┐
                │ Authentication│
                └──────┬───────┘
                       ↓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;┌────────────┐      ┌───────────────┐&lt;br&gt;
│   Client   │ ───→ │    Backend    │&lt;br&gt;
└─────┬──────┘      └───────┬───────┘&lt;br&gt;
      │                     │&lt;br&gt;
      │                     ↓&lt;br&gt;
      │              ┌─────────────┐&lt;br&gt;
      │              │   Firestore │&lt;br&gt;
      │              └──────┬──────┘&lt;br&gt;
      │                     │&lt;br&gt;
      ↓                     ↓&lt;br&gt;
 Local State ←────── Synchronization&lt;/p&gt;

&lt;p&gt;Firestore was no longer just "the place where I save attendance."&lt;/p&gt;

&lt;p&gt;It became one component in a larger system involving:&lt;/p&gt;

&lt;p&gt;authentication&lt;br&gt;
authorization&lt;br&gt;
attendance sessions&lt;br&gt;
device state&lt;br&gt;
offline synchronization&lt;br&gt;
backend validation&lt;br&gt;
caching&lt;br&gt;
read/write optimization&lt;/p&gt;

&lt;p&gt;That changed how I designed everything around it.&lt;/p&gt;

&lt;p&gt;What I'd Do Differently If I Started Again&lt;/p&gt;

&lt;p&gt;If I rebuilt the system from scratch, I would design the data layer before building most of the UI.&lt;/p&gt;

&lt;p&gt;Not because I want a perfect schema from day one.&lt;/p&gt;

&lt;p&gt;Because I'd want to understand the application's major access patterns first.&lt;/p&gt;

&lt;p&gt;I'd write down questions like:&lt;/p&gt;

&lt;p&gt;What does a student need to read?&lt;/p&gt;

&lt;p&gt;What does a teacher need to read?&lt;/p&gt;

&lt;p&gt;What must the backend validate?&lt;/p&gt;

&lt;p&gt;What data changes frequently?&lt;/p&gt;

&lt;p&gt;What data can be cached?&lt;/p&gt;

&lt;p&gt;What must work offline?&lt;/p&gt;

&lt;p&gt;What needs to be queried by date?&lt;/p&gt;

&lt;p&gt;What needs to be queried by student?&lt;/p&gt;

&lt;p&gt;What needs to be queried by attendance session?&lt;/p&gt;

&lt;p&gt;Then I'd design the documents and queries around those questions.&lt;/p&gt;

&lt;p&gt;That would have saved me a lot of unnecessary iteration.&lt;/p&gt;

&lt;p&gt;The Bigger Lesson&lt;/p&gt;

&lt;p&gt;I originally thought my biggest Firestore lesson would be:&lt;/p&gt;

&lt;p&gt;"Don't exceed your free-tier quota."&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;The bigger lesson was:&lt;/p&gt;

&lt;p&gt;Database design starts with application behavior, not just data structure.&lt;/p&gt;

&lt;p&gt;Once I understood how the application actually read and wrote data, several architectural decisions became easier.&lt;/p&gt;

&lt;p&gt;The 50,000-read incident forced me to care about database usage.&lt;/p&gt;

&lt;p&gt;Offline support forced me to care about synchronization.&lt;/p&gt;

&lt;p&gt;Device authentication forced me to care about persistent identity state.&lt;/p&gt;

&lt;p&gt;And together, they pushed me toward thinking about Firestore as part of the architecture rather than just a storage layer.&lt;/p&gt;

&lt;p&gt;That's probably the biggest change in how I build systems now.&lt;/p&gt;

&lt;p&gt;I don't start by asking:&lt;/p&gt;

&lt;p&gt;"How should I store this?"&lt;/p&gt;

&lt;p&gt;I start by asking:&lt;/p&gt;

&lt;p&gt;"How will the system use this?"&lt;/p&gt;

&lt;p&gt;And then I design the data around that answer.&lt;/p&gt;

&lt;p&gt;What's Next?&lt;/p&gt;

&lt;p&gt;So far, this series has gone from:&lt;/p&gt;

&lt;p&gt;Dynamic QR authentication → Firestore quota problems → offline capability → device-bound authentication → database architecture&lt;/p&gt;

&lt;p&gt;But there was another problem hiding in the system.&lt;/p&gt;

&lt;p&gt;Even when the database and backend were working correctly, the application could still feel slow.&lt;/p&gt;

&lt;p&gt;Especially when the backend had to wake up.&lt;/p&gt;

&lt;p&gt;That led me to another engineering problem:&lt;/p&gt;

&lt;p&gt;How do you make a backend with cold starts feel fast to the user?&lt;/p&gt;

&lt;p&gt;That's what I'll cover in the next part.&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>database</category>
    </item>
    <item>
      <title>The QR Code Wasn't Enough: How I Added Device-Bound Authentication</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Sun, 09 Aug 2026 05:34:45 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/the-qr-code-wasnt-enough-how-i-added-device-bound-authentication-41mo</link>
      <guid>https://dev.to/siddarthpatelkama/the-qr-code-wasnt-enough-how-i-added-device-bound-authentication-41mo</guid>
      <description>&lt;p&gt;&lt;em&gt;How I added another layer of verification to make account sharing and proxy attendance harder.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In my previous article, I explained how I used dynamic QR authentication to reduce one of the simplest forms of proxy attendance:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;screenshot and share the QR code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But that introduced another question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What if the student shares their account instead?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Consider this scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Classroom
Student A
   │
   └── Has valid account + trusted device

Hostel
Student B
   │
   └── Gets Student A's credentials
   │
   └── Logs in from another phone
   │
   └── Attempts attendance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The QR code can be valid.&lt;/p&gt;

&lt;p&gt;The credentials can be valid.&lt;/p&gt;

&lt;p&gt;The user can be authenticated.&lt;/p&gt;

&lt;p&gt;And the attendance request can still be illegitimate.&lt;/p&gt;

&lt;p&gt;So I needed another verification layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication Isn't the Same as Device Trust
&lt;/h2&gt;

&lt;p&gt;Traditional authentication primarily answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who is this user?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this attendance system, I also wanted to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is this request coming from the device associated with this account?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That led me to implement device-bound authentication.&lt;/p&gt;

&lt;p&gt;The idea is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Account
     │
     ▼
Trusted Device
     │
     ▼
Attendance Request
     │
     ▼
Backend Verification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The device becomes another signal that the backend can use when deciding whether an attendance request should continue.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Device Registration Flow
&lt;/h2&gt;

&lt;p&gt;At a high level, the flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First Login
    │
    ▼
Generate / Retrieve Device Identifier
    │
    ▼
Associate Device With Account
    │
    ▼
Store Device Association
    │
    ▼
Future Requests
    │
    ▼
Backend Compares Device Identity
    │
    ├── Match ────────► Continue
    │
    └── Mismatch ─────► Reject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the final decision happens on the backend.&lt;/p&gt;

&lt;p&gt;The client shouldn't be able to simply say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Trust me, this is my device."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The backend needs to validate the request against the device association it already knows about.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 1-to-1 Device Lock
&lt;/h2&gt;

&lt;p&gt;In my implementation, the relevant student flow uses a strict one-to-one device association.&lt;/p&gt;

&lt;p&gt;The repository uses the persistent identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uba_emergency_device_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to maintain that device association.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student A
    │
    └── Device A ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student A
    │
    ├── Device A ✅
    │
    └── Device B ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important distinction is that having valid credentials doesn't automatically make a new device trusted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Device Verification Fits
&lt;/h2&gt;

&lt;p&gt;Device binding doesn't replace dynamic QR authentication.&lt;/p&gt;

&lt;p&gt;It adds another layer to it.&lt;/p&gt;

&lt;p&gt;The attendance request becomes something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attendance Request
        │
        ▼
User Authentication
        │
        ▼
Dynamic QR Validation
        │
        ▼
Device Verification
        │
        ▼
Backend Validation
        │
        ▼
Attendance Recorded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer addresses a different problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic QR
&lt;/h3&gt;

&lt;p&gt;Makes previously captured QR codes much less useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Establishes the student's account identity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Device verification
&lt;/h3&gt;

&lt;p&gt;Makes casual account sharing across devices harder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend validation
&lt;/h3&gt;

&lt;p&gt;Keeps the final decision on the server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Not Just Trust the Client?
&lt;/h2&gt;

&lt;p&gt;This is an important principle I learned while building the system:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The client should provide information. The backend should decide whether that information can be trusted.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A device identifier coming from the browser isn't magically secure.&lt;/p&gt;

&lt;p&gt;A determined attacker may be able to manipulate client-side identifiers depending on the environment.&lt;/p&gt;

&lt;p&gt;That's why I don't treat device binding as an unbreakable security mechanism.&lt;/p&gt;

&lt;p&gt;It's another signal.&lt;/p&gt;

&lt;p&gt;The goal is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Make proxy attendance impossible."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The goal is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Make casual proxy attendance significantly harder without making legitimate attendance painful."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Recovery Problem
&lt;/h2&gt;

&lt;p&gt;Device binding immediately creates another problem.&lt;/p&gt;

&lt;p&gt;What happens when the legitimate user changes devices?&lt;/p&gt;

&lt;p&gt;Phones get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lost&lt;/li&gt;
&lt;li&gt;Replaced&lt;/li&gt;
&lt;li&gt;Reset&lt;/li&gt;
&lt;li&gt;Repaired&lt;/li&gt;
&lt;li&gt;Reconfigured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browser storage can also disappear.&lt;/p&gt;

&lt;p&gt;A security mechanism without recovery can become worse than the problem it was designed to solve.&lt;/p&gt;

&lt;p&gt;So a real implementation has to consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trusted Device
      │
      ├── Device lost
      ├── New phone
      ├── Storage reset
      └── Device replacement
              │
              ▼
        Recovery Process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the areas where authentication becomes a system-design problem rather than just an &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security vs Usability
&lt;/h2&gt;

&lt;p&gt;Every additional security layer introduces friction.&lt;/p&gt;

&lt;p&gt;More checks can mean:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More security&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;but also:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More ways to accidentally block legitimate users.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this project, I think about the trade-off as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security
   ×
Usability
   ×
Recoverability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three matter.&lt;/p&gt;

&lt;p&gt;A system that blocks every suspicious request but also locks legitimate students out isn't a good attendance system.&lt;/p&gt;

&lt;p&gt;Likewise, a system that is extremely convenient but accepts every request isn't doing much to prevent proxy attendance.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Doesn't Protect Against
&lt;/h2&gt;

&lt;p&gt;It's important not to oversell device binding.&lt;/p&gt;

&lt;p&gt;It can help reduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic account sharing&lt;/li&gt;
&lt;li&gt;Casual multi-device access&lt;/li&gt;
&lt;li&gt;Credential-based proxy attendance&lt;/li&gt;
&lt;li&gt;Repeated use of one student's account from another device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it doesn't automatically prevent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sophisticated attackers&lt;/li&gt;
&lt;li&gt;Compromised devices&lt;/li&gt;
&lt;li&gt;Manipulation of client-side identifiers&lt;/li&gt;
&lt;li&gt;Someone physically using the registered device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security is about the threat model.&lt;/p&gt;

&lt;p&gt;For my project, I'm primarily trying to prevent &lt;strong&gt;ordinary proxy attendance&lt;/strong&gt;, not defend against a nation-state with a lab full of reverse engineers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Lesson
&lt;/h2&gt;

&lt;p&gt;When I started this project, I thought proxy attendance was mainly a QR-code problem.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;It became an identity problem.&lt;/p&gt;

&lt;p&gt;The QR tells the system about the &lt;strong&gt;attendance session&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Authentication tells it about the &lt;strong&gt;student&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Device verification provides another signal about the &lt;strong&gt;device making the request&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Backend validation brings those signals together.&lt;/p&gt;

&lt;p&gt;That led me to a broader lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Security rarely comes from one perfect mechanism. It comes from multiple layers that compensate for each other's weaknesses.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Device-bound authentication solved another part of the proxy-attendance problem.&lt;/p&gt;

&lt;p&gt;But it also introduced more engineering questions.&lt;/p&gt;

&lt;p&gt;How should these validations interact with the database?&lt;/p&gt;

&lt;p&gt;How do you minimize Firestore reads while performing multiple checks?&lt;/p&gt;

&lt;p&gt;How should suspicious device changes be logged?&lt;/p&gt;

&lt;p&gt;And how do you design the backend so that adding security doesn't make every attendance request unnecessarily expensive?&lt;/p&gt;

&lt;p&gt;Those are the problems I'm exploring as I continue building the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I started with a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I stop someone from sharing a QR code?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then it became:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What stops them from sharing an account?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And eventually:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I know the request is coming from a trusted device?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's been one of the most interesting parts of building this project.&lt;/p&gt;

&lt;p&gt;The architecture wasn't designed perfectly on day one.&lt;/p&gt;

&lt;p&gt;It evolved as each new problem exposed another weakness.&lt;/p&gt;

&lt;p&gt;And honestly, that's probably the most realistic way to build software.&lt;/p&gt;




&lt;h3&gt;
  
  
  Project
&lt;/h3&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/siddarthpatelkama/UBA-veltech-attendance-system" rel="noopener noreferrer"&gt;https://github.com/siddarthpatelkama/UBA-veltech-attendance-system&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💼 &lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/siddarthpatelkama" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>architecture</category>
    </item>
    <item>
      <title>I Didn't Plan to Build an Offline-First Attendance System. Firestore Forced Me To.</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Fri, 24 Jul 2026 19:39:11 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/i-didnt-plan-to-build-an-offline-first-attendance-system-firestore-forced-me-to-259a</link>
      <guid>https://dev.to/siddarthpatelkama/i-didnt-plan-to-build-an-offline-first-attendance-system-firestore-forced-me-to-259a</guid>
      <description>&lt;p&gt;How accidentally exhausting Firestore's free-tier limit transformed a rushed MVP into a more resilient anti-proxy attendance system.&lt;/p&gt;

&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;When I started building an attendance system for my university's UBA (Unnat Bharat Abhiyan) club, I had one clear instruction from our coordinator:&lt;/p&gt;

&lt;p&gt;"Build an MVP as soon as possible."&lt;/p&gt;

&lt;p&gt;Like most student developers, I focused on getting the features working first.&lt;/p&gt;

&lt;p&gt;The application already had everything I needed for the MVP:&lt;/p&gt;

&lt;p&gt;Dynamic QR-based attendance&lt;br&gt;
Firebase Authentication&lt;br&gt;
Firestore database&lt;br&gt;
Anti-proxy validation&lt;br&gt;
Admin dashboard&lt;br&gt;
Meeting management&lt;/p&gt;

&lt;p&gt;My daily workflow looked something like this:&lt;/p&gt;

&lt;p&gt;Code&lt;br&gt;
   ↓&lt;br&gt;
Run&lt;br&gt;
   ↓&lt;br&gt;
Test&lt;br&gt;
   ↓&lt;br&gt;
Scan QR&lt;br&gt;
   ↓&lt;br&gt;
Debug&lt;br&gt;
   ↓&lt;br&gt;
Repeat&lt;/p&gt;

&lt;p&gt;Everything seemed to be progressing smoothly.&lt;/p&gt;

&lt;p&gt;What I wasn't paying attention to was something happening quietly in the background.&lt;/p&gt;

&lt;p&gt;Every refresh.&lt;/p&gt;

&lt;p&gt;Every API call.&lt;/p&gt;

&lt;p&gt;Every test.&lt;/p&gt;

&lt;p&gt;Every debugging session.&lt;/p&gt;

&lt;p&gt;All of them were consuming Firestore document reads.&lt;/p&gt;

&lt;p&gt;The Problem I Never Expected&lt;/p&gt;

&lt;p&gt;One day, while testing the application, things suddenly stopped working.&lt;/p&gt;

&lt;p&gt;Attendance requests started failing.&lt;/p&gt;

&lt;p&gt;Firestore wasn't responding.&lt;/p&gt;

&lt;p&gt;Like most developers, my first thought was:&lt;/p&gt;

&lt;p&gt;"I definitely broke something."&lt;/p&gt;

&lt;p&gt;I checked my recent commits.&lt;/p&gt;

&lt;p&gt;I reviewed the API routes.&lt;/p&gt;

&lt;p&gt;I verified Firebase Authentication.&lt;/p&gt;

&lt;p&gt;I even looked through my Firestore Security Rules.&lt;/p&gt;

&lt;p&gt;Everything looked perfectly fine.&lt;/p&gt;

&lt;p&gt;Then I opened the Firebase Console.&lt;/p&gt;

&lt;p&gt;That's when I saw it.&lt;/p&gt;

&lt;p&gt;Firestore Reads: 50,000 / 50,000&lt;/p&gt;

&lt;p&gt;I had exhausted the entire free-tier quota.&lt;/p&gt;

&lt;p&gt;Not because thousands of students were using my application.&lt;/p&gt;

&lt;p&gt;Not because it had gone viral.&lt;/p&gt;

&lt;p&gt;Simply because I had spent days developing and debugging it.&lt;/p&gt;

&lt;p&gt;Ironically, I had become my own biggest user.&lt;/p&gt;

&lt;p&gt;The Easy Fix Wasn't the Right Fix&lt;/p&gt;

&lt;p&gt;Technically, I could have waited for the daily quota to reset.&lt;/p&gt;

&lt;p&gt;Problem solved.&lt;/p&gt;

&lt;p&gt;But this wasn't just another side project.&lt;/p&gt;

&lt;p&gt;It was an attendance system intended to be used during actual UBA meetings.&lt;/p&gt;

&lt;p&gt;If Firestore became unavailable during a meeting, attendance couldn't simply stop.&lt;/p&gt;

&lt;p&gt;So instead of asking,&lt;/p&gt;

&lt;p&gt;"How do I reduce Firestore reads?"&lt;/p&gt;

&lt;p&gt;I asked a different question.&lt;/p&gt;

&lt;p&gt;"How do I make the system continue working even when Firestore isn't available?"&lt;/p&gt;

&lt;p&gt;That single question completely changed the architecture of the project.&lt;/p&gt;

&lt;p&gt;Offline-First Was Never the Plan&lt;/p&gt;

&lt;p&gt;Before this happened, offline support wasn't even on my roadmap.&lt;/p&gt;

&lt;p&gt;Every attendance request depended on Firestore.&lt;/p&gt;

&lt;p&gt;Every scan required the backend.&lt;/p&gt;

&lt;p&gt;Which meant one dependency controlled whether the entire application worked.&lt;/p&gt;

&lt;p&gt;I didn't like that.&lt;/p&gt;

&lt;p&gt;So I redesigned the workflow.&lt;/p&gt;

&lt;p&gt;Instead of immediately writing attendance to Firestore, the application could now continue functioning locally whenever the backend wasn't available.&lt;/p&gt;

&lt;p&gt;Attendance records would be stored securely on the device and synchronised automatically once connectivity or database access returned.&lt;/p&gt;

&lt;p&gt;The project unexpectedly became offline-first.&lt;/p&gt;

&lt;p&gt;But There Was One Catch...&lt;/p&gt;

&lt;p&gt;Building offline support for a normal CRUD application is relatively straightforward.&lt;/p&gt;

&lt;p&gt;Building it for an anti-proxy attendance system isn't.&lt;/p&gt;

&lt;p&gt;Offline mode couldn't become an easy way to bypass the security model.&lt;/p&gt;

&lt;p&gt;The system still had to guarantee:&lt;/p&gt;

&lt;p&gt;Attendance integrity&lt;br&gt;
Duplicate prevention&lt;br&gt;
Secure local storage&lt;br&gt;
Reliable synchronisation&lt;br&gt;
Consistent validation after syncing&lt;/p&gt;

&lt;p&gt;The goal wasn't simply to make the application work offline.&lt;/p&gt;

&lt;p&gt;It was to ensure the same anti-proxy workflow remained intact whether the application was online or offline.&lt;/p&gt;

&lt;p&gt;That made the redesign much more interesting than simply adding a cache.&lt;/p&gt;

&lt;p&gt;Solving the Symptom vs Solving the Cause&lt;/p&gt;

&lt;p&gt;The offline architecture solved the immediate problem.&lt;/p&gt;

&lt;p&gt;But after everything was working again, I realised something.&lt;/p&gt;

&lt;p&gt;Running out of Firestore reads wasn't the real problem.&lt;/p&gt;

&lt;p&gt;My application was.&lt;/p&gt;

&lt;p&gt;I had built the MVP quickly.&lt;/p&gt;

&lt;p&gt;I hadn't built it efficiently.&lt;/p&gt;

&lt;p&gt;That led me to review every interaction between the application and Firestore.&lt;/p&gt;

&lt;p&gt;Questions I had never asked before suddenly became important.&lt;/p&gt;

&lt;p&gt;Do I really need this document read?&lt;br&gt;
Why am I fetching the same document multiple times?&lt;br&gt;
Can this data be cached?&lt;br&gt;
Can I combine multiple reads into one request?&lt;br&gt;
Is every database write actually necessary?&lt;/p&gt;

&lt;p&gt;Those questions ended up improving the architecture far more than any new feature.&lt;/p&gt;

&lt;p&gt;Optimising Firestore Usage&lt;/p&gt;

&lt;p&gt;Once the offline system was stable, I shifted my attention to optimisation.&lt;/p&gt;

&lt;p&gt;I reduced unnecessary Firestore reads.&lt;/p&gt;

&lt;p&gt;I optimised database queries.&lt;/p&gt;

&lt;p&gt;I removed redundant writes wherever possible.&lt;/p&gt;

&lt;p&gt;I introduced Render's free cache to store frequently accessed data, reducing repeated requests for information that rarely changed.&lt;/p&gt;

&lt;p&gt;These weren't flashy improvements.&lt;/p&gt;

&lt;p&gt;Users would never notice them directly.&lt;/p&gt;

&lt;p&gt;But together, they made the application significantly more efficient and much less dependent on Firestore's free-tier limits.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;p&gt;When I started this project, I thought software engineering was mostly about building features.&lt;/p&gt;

&lt;p&gt;This experience taught me something different.&lt;/p&gt;

&lt;p&gt;Sometimes the most important engineering decisions don't happen while adding new features.&lt;/p&gt;

&lt;p&gt;They happen when your assumptions break.&lt;/p&gt;

&lt;p&gt;Running out of Firestore reads forced me to think beyond "making it work."&lt;/p&gt;

&lt;p&gt;It pushed me to think about resilience.&lt;/p&gt;

&lt;p&gt;Reliability.&lt;/p&gt;

&lt;p&gt;Efficiency.&lt;/p&gt;

&lt;p&gt;And designing systems that continue working even when one of their dependencies doesn't.&lt;/p&gt;

&lt;p&gt;Looking back, hitting Firestore's limit was frustrating.&lt;/p&gt;

&lt;p&gt;Today, I'm actually grateful it happened.&lt;/p&gt;

&lt;p&gt;Without that unexpected limitation, I probably would never have redesigned the application into something far more resilient than the MVP I originally planned.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;This project began as a simple anti-proxy attendance system for my university.&lt;/p&gt;

&lt;p&gt;It unexpectedly became a lesson in building software that can survive constraints.&lt;/p&gt;

&lt;p&gt;Sometimes architecture evolves because of careful planning.&lt;/p&gt;

&lt;p&gt;Sometimes it evolves because reality forces you to rethink everything.&lt;/p&gt;

&lt;p&gt;For me, it took exactly 50,000 Firestore document reads to learn that lesson.&lt;/p&gt;

&lt;p&gt;And honestly, I wouldn't build it any other way now.&lt;/p&gt;

&lt;p&gt;Thanks for Reading! 🚀&lt;/p&gt;

&lt;p&gt;Have you ever hit a cloud free-tier limit, API quota, or infrastructure constraint that completely changed the way you designed your application?&lt;/p&gt;

&lt;p&gt;I'd love to hear your experience in the comments. Some of the best engineering lessons come from the problems we never planned for.&lt;/p&gt;

</description>
      <category>database</category>
      <category>firebase</category>
      <category>programming</category>
      <category>software</category>
    </item>
    <item>
      <title>How I Designed an Anti-Proxy Attendance System Using Dynamic QR Authentication</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Mon, 29 Jun 2026 12:45:23 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/how-i-designed-an-anti-proxy-attendance-system-using-dynamic-qr-authentication-4h8i</link>
      <guid>https://dev.to/siddarthpatelkama/how-i-designed-an-anti-proxy-attendance-system-using-dynamic-qr-authentication-4h8i</guid>
      <description>&lt;p&gt;&lt;em&gt;"Anyone can generate a QR code. Designing one that actually prevents misuse is the hard part."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When people hear "QR-based attendance," the first question is usually:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can't someone just take a photo of the QR code and send it to their friend?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is: &lt;strong&gt;yes&lt;/strong&gt;, if the system is designed poorly.&lt;/p&gt;

&lt;p&gt;That question became one of the biggest engineering challenges while building my attendance platform.&lt;/p&gt;

&lt;p&gt;Instead of thinking about QR codes as images, I started thinking about them as &lt;strong&gt;temporary authentication tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Most QR attendance systems work like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Teacher generates a QR code.&lt;/li&gt;
&lt;li&gt;Students scan it.&lt;/li&gt;
&lt;li&gt;Attendance is marked.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Unfortunately, it's also vulnerable.&lt;/p&gt;

&lt;p&gt;A student can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Photograph the QR code.&lt;/li&gt;
&lt;li&gt;Send it through WhatsApp.&lt;/li&gt;
&lt;li&gt;Another student scans it from somewhere else.&lt;/li&gt;
&lt;li&gt;Attendance gets recorded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The QR code itself becomes the vulnerability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rethinking the QR Code
&lt;/h2&gt;

&lt;p&gt;Instead of creating a QR code that simply points to an attendance page, I designed it to behave more like a short-lived authentication token.&lt;/p&gt;

&lt;p&gt;Every QR code is generated dynamically and remains valid only for a limited period (in my implementation, I set this to 11 seconds).&lt;/p&gt;

&lt;p&gt;Once it expires, the system rejects it.&lt;/p&gt;

&lt;p&gt;This dramatically reduces the opportunity for someone to reuse an older QR code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication Beyond the QR
&lt;/h2&gt;

&lt;p&gt;Scanning the QR code alone isn't enough.&lt;/p&gt;

&lt;p&gt;The backend performs additional validation before marking attendance.&lt;/p&gt;

&lt;p&gt;Rather than trusting the client, the server becomes the source of truth.&lt;/p&gt;

&lt;p&gt;This reduces opportunities for client-side manipulation and keeps the attendance workflow under server control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing for the Real World
&lt;/h2&gt;

&lt;p&gt;One challenge I encountered was balancing &lt;strong&gt;security&lt;/strong&gt; with &lt;strong&gt;usability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the QR expires too quickly, students may struggle to scan it in time.&lt;/p&gt;

&lt;p&gt;If it stays active for too long, it becomes easier to misuse.&lt;/p&gt;

&lt;p&gt;Finding the right balance required thinking about actual classroom conditions rather than only technical implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;One lesson stood out throughout this project:&lt;/p&gt;

&lt;p&gt;Security isn't usually one big feature.&lt;/p&gt;

&lt;p&gt;It's the result of multiple smaller decisions working together.&lt;/p&gt;

&lt;p&gt;A dynamic QR code helps.&lt;/p&gt;

&lt;p&gt;Server-side validation helps.&lt;/p&gt;

&lt;p&gt;Short-lived sessions help.&lt;/p&gt;

&lt;p&gt;Each layer reduces risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This article only covers one piece of the system.&lt;/p&gt;

&lt;p&gt;In future posts, I'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend architecture&lt;/li&gt;
&lt;li&gt;Firestore data modeling&lt;/li&gt;
&lt;li&gt;Authentication flow&lt;/li&gt;
&lt;li&gt;Scaling the platform&lt;/li&gt;
&lt;li&gt;Engineering decisions behind the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building software has taught me that writing code is only part of engineering.&lt;/p&gt;

&lt;p&gt;Designing systems that continue to work under real-world conditions is where the interesting problems begin. In this project, I used libraries like &lt;code&gt;qrcode&lt;/code&gt; on the backend to generate dynamic QR codes and &lt;code&gt;html5-qrcode&lt;/code&gt; on the frontend to scan and verify them efficiently, while SHA-256 hashing was used on the backend to securely generate and validate the QR token payloads.&lt;/p&gt;




&lt;p&gt;⭐ GitHub: &lt;a href="https://github.com/siddarthpatelkama" rel="noopener noreferrer"&gt;https://github.com/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💼 LinkedIn: &lt;a href="https://www.linkedin.com/in/siddarthpatelkama" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>showdev</category>
      <category>softwareengineering</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Building an Anti-Proxy -Offline-First Attendance Platform with Next.js, Express &amp; Firebase</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Fri, 26 Jun 2026 12:10:14 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/building-an-anti-proxy-offline-first-attendance-platform-with-nextjs-express-firebase-15io</link>
      <guid>https://dev.to/siddarthpatelkama/building-an-anti-proxy-offline-first-attendance-platform-with-nextjs-express-firebase-15io</guid>
      <description>&lt;p&gt;Traditional attendance systems seem simple until you look at how they work in practice. Manual roll calls consume valuable class time, paper attendance sheets are inefficient, and many QR-based systems can still be exploited through proxy attendance.&lt;/p&gt;

&lt;p&gt;I wanted to explore whether a better solution was possible.&lt;/p&gt;

&lt;p&gt;This project is my attempt at designing and building an &lt;strong&gt;offline-first anti-proxy attendance platform&lt;/strong&gt; from scratch as a solo developer. The goal wasn't just to digitize attendance, but to create a system that remains reliable in real-world environments while prioritizing security, scalability, and a smooth user experience.&lt;/p&gt;

&lt;p&gt;The platform is built using &lt;strong&gt;Next.js 16, React 19, TypeScript, Express.js, Firebase Authentication, Firestore, Firebase Admin SDK, Tailwind CSS, Capacitor, Progressive Web Apps (PWA), Sentry, and dynamic QR-based authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than focusing only on features, this article explores the engineering decisions behind the project: why I chose an offline-first architecture, how dynamic QR validation improves security, the reasoning behind the technology stack, the challenges I faced during development, and the lessons I learned while building a production-style application that is already being used in my college club.&lt;/p&gt;

&lt;p&gt;The project is still evolving, and I continue to improve it whenever I have time. My aim is to keep documenting that journey by sharing not only what works, but also the mistakes, trade-offs, and architectural decisions that shaped the system.&lt;/p&gt;

&lt;p&gt;If you're interested in &lt;strong&gt;full-stack development, system design, Next.js, Firebase, TypeScript, or building software that solves practical problems&lt;/strong&gt;, I hope you'll find something valuable in this article.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/siddarthpatelkama" rel="noopener noreferrer"&gt;https://github.com/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💼 &lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/siddarthpatelkama" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm always open to feedback, suggestions, and discussions with fellow developers. If you have ideas for improving the project or questions about the implementation, I'd love to hear from you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>computerscience</category>
      <category>development</category>
      <category>vibecoding</category>
    </item>
  </channel>
</rss>
