How A Simple Assignment Became A Full-Stack Automated System (And Why Vercel's Timeout Message Haunts Me)

CertainlyMohneeshCertainlyMohneesh

TL;DR: Built a MGNREGA dashboard for one state. Turned into automated system for all of India. Fought timeouts, laggy maps, and CSV hell. Won using GitHub Actions, CSS Grid, and zero paid features.

Day 0: The Assignment (Not the official Days, I didn't count them then)

Bharat Digital Fellowship 2026 Assignment:

  • Choose a state (Maharashtra, Bihar, etc.)
  • Build a dashboard with MGNREGA data
  • Add district comparison tool
  • Deadline: November 1st, 2025

My reaction: "Easy. I'll pick Maharashtra, throw up a React dashboard, add some charts. Done in 1 week."

Narrator: It was not done in around 5 days.

Day 1: The Data Nightmare

I logged into data.gov.in to fetch MGNREGA data.

My plan:

  1. Call API
  2. Get all data
  3. Display in dashboard
  4. Submit assignment

Reality:

fetch('https://api.data.gov.in/resource/...')
  .then(res => res.json())
  .then(data => {
    // Still fetching...
    // Still fetching...
    // Still fetching...
    // *30 minutes later*
    // Still fetching...
  });

The API wasn't slow. The data was massive.

The Numbers That Broke My Fetch Call

  • 28,988 total metrics across all states
  • 14,028 unique metrics after deduplication
  • 740 districts across 36 states/UTs
  • 35+ data points per district per month
  • Updates: Daily

Problem: Fetching all this via API = infinite loop of doom.

Day 1.5: The CSV Pivot

New plan:

  1. Screw the API (for now)
  2. Download CSV files manually from data.gov.in
  3. Import to database
  4. Build dashboard from local data

Downloaded:

  • MAHARASHTRA_2024-2025.csv (19 months of data)
  • MAHARASHTRA_2025-2026.csv (latest October data)

Result:

  • 646 monthly records
  • 34 districts
  • Database imported successfully ✅
  • Dashboard built ✅
  • Submission on Nov 1st ✅

Assignment complete. Time to relax, right?

Me: "...but what if I did this for ALL of India?"

Day 2: The "Just For Fun" Spiral

Date: November 2nd, 2025

Time: 3 AM (all bad decisions happen at 3 AM)

Thought: "I can add filters to the API for all states and inject that data."

The All-India Challenge

Instead of just Maharashtra, let's get:

  • All 36 states and UTs
  • Both 2024-25 and 2025-26 financial years
  • All 740 districts
  • All 14,028 unique metrics

Added API filters:


for (const state of states) {
  for (const year of financialYears) {
    await fetchAndInjectData(state, year);
  }
}

Result: Data injected successfully into Supabase! 🎉

Then reality hit: This data updates daily.

Manual updates? Hell no.

Day 2.5: The Cron Job That Never Was

The Plan: Add a cron job to sync data daily at 1 AM IST.

The Problem: Deployed on Vercel. Cron jobs = Paid feature. I'm broke.

New Plan: Use Vercel's Edge Functions with external cron service (cron-job.org) → Free!

// /api/cron/daily-sync
export async function GET(request: Request) {
  // Verify secret
  // Fetch latest data
  // Update database
  return Response.json({ success: true });
}

GitHub Actions workflow:

schedule:
  - cron: '30 19 * * *' # 1:00 AM IST

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - name: Call sync endpoint
        run: |
          curl -X GET "https://your-app.vercel.app/api/cron/daily-sync?secret=${{ secrets.CRON_SECRET }}"

Deployed. Triggered. Watched logs.

✅ GitHub Action started
✅ API endpoint called
⏰ Syncing data...
⏰ Still syncing...
⏰ Still syncing...
❌ Error: Function execution timeout (300 seconds)

Me: "WHAT."

Day 3: The Vercel Wall

The Problem:

  • Vercel's max function timeout: 300 seconds (5 minutes)
  • My data sync: 15-60 minutes depending on API rate limits
  • Math: 15 minutes > 5 minutes → ❌ Timeout

Options:

  1. Pay for Vercel Pro (timeout up to 900s / 15 min) — $$$
  2. Split sync into smaller chunks — Complex, fragile
  3. Run sync directly on GitHub Actions — Free, reliable, no timeout

Guess which one I picked.

The Real Solution: Direct Database Sync

New architecture:

GitHub Actions Runner
  ↓
  Install Node.js, pnpm
  ↓
  pnpm install (frozen lockfile)
  ↓
  Generate Prisma Client
  ↓
  Run scripts/daily-sync.ts
  ↓
  Connect directly to Supabase PostgreSQL
  ↓
  Sync data (15-60 min, no timeout)
  ↓
  Upload logs as artifacts

Key changes:

# .github/workflows/daily-sync.yml
jobs:
  sync-data:
    runs-on: ubuntu-latest
    timeout-minutes: 180 # 3 hours (GitHub's max)
    
    steps:
      - name: Enable pnpm
        run: corepack enable && corepack prepare pnpm@latest --activate
        
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
        
      - name: Generate Prisma Client
        run: pnpm prisma generate
        
      - name: Run sync
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          DATA_GOV_API_KEY: ${{ secrets.DATA_GOV_API_KEY }}
        run: pnpm run sync:cli

Result:

  • ✅ Runs daily at 1:00 AM IST
  • ✅ No timeouts (3-hour limit)
  • ✅ Full logs uploaded as artifacts
  • ✅ Resume support (if sync fails midway)
  • ✅ Completely free (GitHub Free Tier)

Lesson learned: Sometimes the "hacky" solution is the right solution.

Day 3.5: The Map Saga (Or: When Simple Beats Complex)

Goal: Interactive India map where users can click states and navigate to state pages.

Attempt 1: The SVG Dream

Found: India GeoJSON file with precise state boundaries

Size: 210KB

Coordinates: Thousands of polygon points

Used: react-simple-maps library

<ComposableMap>
  <Geographies geography={indiaGeoJSON}>
    {({ geographies }) =>
      geographies.map((geo) => (
        <Geography
          key={geo.rsmKey}
          geography={geo}
          onClick={() => navigate(`/state/${geo.id}`)}
        />
      ))
    }
  </Geographies>
</ComposableMap>

Result:

  • ✅ Looks beautiful
  • ✅ Precise boundaries
  • ❌ Laggy as hell on mobile
  • ❌ 19MB file size
  • ❌ Re-renders on every hover
  • ❌ Unusable on ₹3,000 phones

Me: "There has to be a better way."

Attempt 2: CSS Grid Simplicity

Idea: What if... I just use CSS Grid?

Implementation:

  • 27 rows × 32 columns grid
  • Each state = series of grid cells
  • Cells merge using grid-column and grid-row
  • Click handlers on each state div
  • Tooltip on hover
// Simplified example
<div className="grid grid-cols-32 grid-rows-27">
  {/* Kashmir */}
  <div
    className="col-start-13 col-span-7 row-start-1 row-span-5"
    onClick={() => navigate('/state/jammu-kashmir')}
  >
    Jammu & Kashmir
  </div>
  {/* Repeat for all 36 states */}
</div>

Result:

  • ✅ Instant load
  • ✅ Zero lag on mobile
  • ✅ 5KB CSS (vs 210KB GeoJSON)
  • ✅ Works on ancient phones
  • ✅ Easy to maintain

Trade-off: Not geographically precise, but functional and fast.

Lesson: Fancy isn't always better. Sometimes CSS Grid > Complex SVG.

Day 4: Search Bar With Voice (Because Why Not)

Goal: Search districts by name with voice input.

Why voice? Not everyone types well. Especially older users or regional language speakers.

Tech Stack:

  • Fuse.js for fuzzy search (handles typos)
  • Web Speech API (browser-native, zero dependencies)
  • Keyboard navigation (↑↓ arrows, Enter, Escape)

Implementation:

// Voice input
const recognition = new webkitSpeechRecognition();
recognition.lang = 'en-IN'; // or hi-IN, mr-IN, etc.
recognition.onresult = (event) => {
  const transcript = event.results[0][0].transcript;
  setSearchQuery(transcript); // Auto-searches
};

// Fuzzy search
const fuse = new Fuse(districts, {
  keys: ['name', 'stateName'],
  threshold: 0.3, // 30% tolerance for typos
});

const results = fuse.search(searchQuery);

Features added:

  • 🎤 Voice input button (click to speak)
  • ⌨️ Keyboard navigation (arrows to move, Enter to select)
  • 🔍 Fuzzy matching (handles typos like "Mumbia" → "Mumbai")
  • 📱 Mobile-optimized (large touch targets)

Result: Search works in 9 languages with voice support. Zero external paid APIs.

Day 4.5: The i18n Rabbit Hole

Realization: 1.4 billion Indians don't all speak English.

Solution: Multi-language support.

Languages Added:

  1. English (en)
  2. Hindi (hi)
  3. Marathi (mr)
  4. Tamil (ta)
  5. Telugu (te)
  6. Malayalam (ml)
  7. Kannada (kn)
  8. Bengali (bn)
  9. Gujarati (gu)

Tool: next-intl for routing + i18n

Translation process:

  1. Extract all UI strings (25+ keys)
  2. Use AI (ChatGPT/Claude) for initial translations
  3. Manual review by native speakers (where possible)
  4. Store in JSON files (messages/en.jsonmessages/hi.json, etc.)

Did I use AI? Yes. Obviously.

Why? Because manually translating 25 keys × 9 languages = 225 strings. Ain't nobody got time for that.

But: AI translations need review. Some phrases don't translate well. Cultural context matters.

Example:

  • English: "Explore Districts"
  • Hindi (AI): "जिलों का अन्वेषण करें" (technically correct)
  • Hindi (Better): "जिले देखें" (more natural)

Lesson: Use AI as a starting point, not the finish line.

Next Chapter: The Next Evolution - Golang Backend (The 10x Future)

Current status: Working system. Data syncs daily. APIs respond fast. Users happy.

But here's the thing: I'm already hitting limits.

The Current Bottlenecks

1. Sync Time

  • Current: 15-60 minutes for 740 districts
  • GitHub Actions timeout: 180 minutes max
  • Buffer: Only 2-3x (uncomfortable)

2. Memory Usage

  • Current: 500MB during sync
  • GitHub runner limit: 7GB (but shared)
  • Vercel function limit: 1GB (serverless constraint)

3. Concurrency

  • TypeScript Promise.all: 3-5 concurrent operations max
  • Reason: Event loop saturation, memory pressure
  • Result: Slow throughput

4. Cost

  • Vercel Pro: $20/month (for cron jobs)
  • Supabase Pro: $25/month (approaching limits)
  • Total: $540/year (for a side project!)

The Golang Solution (The 10x Rewrite)

I'm planning a backend migration to Golang while keeping the TypeScript frontend. Here's why this isn't just "rewrite for fun":

1. Concurrency Model (The Game Changer)

Current TypeScript limitation:

// Max 3-5 concurrent operations before event loop saturates
await Promise.all(states.slice(0, 3).map(s => syncState(s)));

Golang goroutines:

// 50-100 concurrent operations easily
var wg sync.WaitGroup
for _, state := range states {
    wg.Add(1)
    go func(s State) {
        defer wg.Done()
        syncState(s) // Each runs in parallel
    }(state)
}
wg.Wait()

Real impact:

  • Sync time: 15-60 min → 5-15 min (3-4x faster)
  • Throughput: 3 req/s → 50 req/s (16x)
  • GitHub Actions: More buffer for future data growth

2. Memory Efficiency (Critical at Scale)

Current issue:

  • Loading 28,988 metrics → 500MB RAM
  • Node.js garbage collection pauses
  • Memory spikes during sync

Golang stream processing:

rows, _ := db.Query("SELECT * FROM metrics WHERE finYear = ?", year)
defer rows.Close()

for rows.Next() { // One row at a time
    var metric Metric
    rows.Scan(&metric.ID, &metric.Value, ...)
    processAndInsert(metric) // Immediate processing
    // Memory freed after each iteration
}

Result:

  • Memory usage: Constant 100-150MB (3-4x less)
  • No GC pauses: Golang GC is microsecond-level
  • Predictable performance

3. Deployment Simplicity

Current (Next.js + TypeScript):

FROM node:20-alpine          # 150MB
COPY package.json .
RUN npm install              # +200MB node_modulesCOPY . .
RUN npm run build            # +50MB build artifacts# Final image: 400MB+

Golang (Single Binary):

FROM scratch                 # 0MB base
COPY main /main              # 15MB binary (all dependencies included)CMD ["/main"]
# Final image: 15MB

Benefits:

  • Deploy: Just copy one file to server
  • Cold start: < 100ms (vs 2-3s for Node.js)
  • No dependency hell: Everything compiled in

4. Cost Optimization

Current annual costs:

ServiceCurrentWith GolangSavings
ComputeVercel Pro $240VPS $60$180
DatabaseSupabase $300Supabase Free $0$300
GitHub Actions$0 (barely)$0 (comfortable)Buffer
Total$540/year$60/year$480

Why savings?

  • Golang uses 5x less resources = Supabase stays in free tier
  • Self-host on $5/month VPS instead of Vercel Pro
  • Faster sync = more GitHub Actions free tier headroom

5. Real-World Validation

Companies that migrated Node.js → Golang:

PayPal:

  • 35% faster response time
  • 50% fewer servers needed
  • Handled 10x more requests

Uber:

  • Geofence service: 10x concurrency
  • 80% memory reduction
  • No more OOM crashes

Medium:

  • API response: 500ms → 50ms
  • User-facing pages 10x faster
  • Happier users

The pattern: Heavy data systems benefit massively from Golang.

6. The Proposed Architecture

Frontend (Keep TypeScript/Next.js)
  ├── UI components
  ├── i18n (9 languages)
  ├── Voice search
  ├── Interactive map
  └── Deployed on Vercel (static/SSR)

Backend (Migrate to Golang)
  ├── REST API server (Fiber/Gin)
  │   ├── /api/districts
  │   ├── /api/compare
  │   └── /api/states
  │
  ├── Daily sync service
  │   ├── 50 concurrent workers
  │   ├── Batch inserts (1000 rows)
  │   └── Auto-retry logic
  │
  └── Deployed on $5 VPS or Fly.io

Database (Same)
  └── PostgreSQL (Supabase or self-hosted)

Cache (Same)
  └── Redis (Upstash or self-hosted)

Key insight: Keep TypeScript where it shines (frontend), use Golang where performance matters (backend).

Implementation Roadmap

Month 1: Golang API Server

  • Replace Next.js API routes with Golang
  • Deploy both (Next.js frontend + Golang API)
  • Zero downtime migration

Month 2: Golang Sync Service

  • Replace TypeScript daily sync script
  • Deploy as separate microservice
  • 10x faster sync times

Month 3: Optimization

  • Horizontal scaling (run multiple instances)
  • Advanced caching strategies
  • Monitoring (Prometheus + Grafana)

Month 4: Cost Optimization

  • Move to self-hosted VPS
  • Annual cost: $540 → $60

Why This Matters

This isn't just about performance.

This is about sustainability for a side project that could grow into something real.

The questions I ask myself:

  • What if 10,000 users start using this daily?
  • What if government wants to partner and needs SLAs?
  • What if data grows to 100,000+ districts (all of rural India)?
  • What if I want to add real-time updates?

With TypeScript alone: Expensive, slow, hitting limits.

With Golang backend: Cheap, fast, scales horizontally.

Learning Plan (Public Commitment)

I'm learning Golang by building in public:

Week 1-2: Golang basics

  • Tour of Go (official tutorial)
  • Concurrency patterns (goroutines, channels)
  • Standard library exploration

Week 3-4: Build simple API

  • Fiber/Gin framework (Express-like)
  • Database connections (pgx, GORM)
  • API endpoints (REST)

Week 5-6: Build sync service

  • Worker pool pattern
  • Batch processing
  • Error handling & retries

Week 7-8: Deploy to production

  • Dockerize
  • Deploy to Fly.io (or VPS)
  • Monitor performance

Timeline: 2 months to production Golang backend.

The End Goal

A hybrid system:

  • TypeScript frontend (React ecosystem, rapid iteration)
  • Golang backend (performance, cost-efficiency, scalability)
  • Best of both worlds

This is how side projects evolve into production-grade systems.

This is how you build for scale without venture capital.

This is the future of this project.

Stay tuned for the Golang migration series. I'll document everything: wins, losses, and "why did I think this was a good idea" moments. 😅

The Tech Stack (Final)

Frontend:
  - Next.js 16 (App Router)
  - React 19.2
  - TypeScript 5
  - TailwindCSS 4
  - Framer Motion (animations)
  - Fuse.js (search)
  - Web Speech API (voice)
  - next-intl (i18n)

Backend:
  - Next.js API Routes
  - PostgreSQL (Supabase)
  - Prisma ORM 6

DevOps:
  - GitHub Actions (automated sync)
  - Vercel (hosting)
  - pnpm (package manager)

Data Source:
  - data.gov.in API
  - Manual CSV imports

Total Cost: $0/month (Free tiers everywhere)

Unknown Chapter: The Map That Nobody Asked For (But I Built Anyway)

Remember that 19MB SVG map that was mocking me?

It's getting a proper funeral. And a rebirth. As an interactive Leaflet map.

The Unnecessary Journey Begins

Rational Brain: "The CSS Grid map works fine. Users can click states. It's functional. Move on."

Developer Brain: "But what if... zoom controls? Tooltips? Choropleth visualization? Fullscreen mode?"

Rational Brain: "That's overkill for employment data."

Developer Brain: "CHALLENGE ACCEPTED."


Why Leaflet? (The Technical Justification)

Current CSS Grid Map:

  • ✅ Works
  • ✅ Fast
  • ✅ Simple
  • ❌ No zoom/pan
  • ❌ No hover tooltips
  • ❌ Not "industry standard"

Leaflet Interactive Map:

  • ✅ Zoom/pan controls
  • ✅ Rich hover tooltips
  • ✅ Choropleth color scales
  • ✅ Fullscreen mode
  • ✅ Mobile gestures (pinch-to-zoom)
  • ✅ Export as image
  • ⚠️ Slightly more complex

The Trade-off:

  • Complexity: +30%
  • User Experience: +200%
  • Coolness Factor: +1000%

Decision: Worth it.


The Open Source Moment

I needed GeoJSON files for all 36 Indian states with district boundaries.

Option 1: Process raw shapefiles myself

  • Download from government sources
  • Convert SHP → GeoJSON
  • Simplify coordinates
  • Test each file
  • Time: 2-3 weeks

Option 2: Search GitHub

  • Type: "india geojson districts"
  • Find: udit-001/india-maps-data
  • Files ready: All 36 states ✅
  • Time: 5 minutes

This. This is why I love open source.

Discovering india-maps-data

Repository: https://github.com/udit-001/india-maps-data

What I found:

geojson/
├── india.geojson              # All India with state boundaries
└── states/
    ├── maharashtra.geojson    # Districts of Maharashtra
    ├── tamil-nadu.geojson     # Districts of Tamil Nadu
    ├── uttar-pradesh.geojson  # Districts of UP
    └── ... (all 36 states!)

CDN URLs (no download needed):

https://cdn.jsdelivr.net/gh/udit-001/india-maps-data@latest/geojson/india.geojson
https://cdn.jsdelivr.net/gh/udit-001/india-maps-data@latest/geojson/states/maharashtra.geojson

My reaction: 🤯

Someone already did the hard work. Clean files. CDN hosted. Free. MIT licensed.

What I saved:

  • 2 weeks of map data processing
  • Countless hours debugging coordinate systems
  • Sanity dealing with QGIS/MapShaper

What I learned: Before building, search GitHub. Seriously. Just search.


The UI Inspiration

While researching Leaflet implementations, I found another gem:

Repository: https://github.com/udit-001/india-maps

An interactive India map built with:

  • React + Vite
  • Leaflet + react-leaflet
  • GeoJSON rendering
  • Color customization
  • Export functionality

I didn't copy the code. But I studied:

  • How they handled GeoJSON loading
  • Tooltip implementations
  • Color scale logic
  • Mobile responsiveness
  • State name normalization

Open source isn't just about code. It's about learning patterns, seeing solutions, understanding trade-offs.


The Implementation Plan

Architecture:

/map                    → All India state-level map
/map/[stateCode]        → Individual state district map

Data Flow:

User clicks state on home page
    ↓
/map (All India Leaflet map)
    ↓ (clicks Maharashtra)
/map/maharashtra (District-level map)
    ↓ (clicks district card)
/state/maharashtra (Existing state page)

Tech Stack:

// Dependencies
import { MapContainer, GeoJSON, ZoomControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';

// Data
const geoData = await fetch(
  'https://cdn.jsdelivr.net/gh/udit-001/india-maps-data@latest/geojson/india.geojson'
).then(r => r.json());

// Visualization
const getColor = (expenditure) => {
  if (exp > 1000000000) return '#0f766e'; // High
  if (exp > 500000000) return '#14b8a6';  // Medium
  if (exp > 100000000) return '#5eead4';  // Low
  return '#ccfbf1'; // Very low
};

Features:

  1. Choropleth Visualization

    • Color states by expenditure
    • Dark teal = High, Light teal = Low
    • Gray = No data
  2. Interactive Tooltips

    Maharashtra
    ━━━━━━━━━━━━━━━━
    Districts: 36
    Expenditure: ₹2,450.56 Cr
    Households: 1,23,456
    Click to drill down →
    
  3. Controls

    • Zoom in/out
    • Reset view
    • Fullscreen toggle
    • Metric switcher (expenditure/households/person days)
  4. Mobile Gestures

    • Pinch to zoom
    • Two-finger pan
    • Tap for details

The "Is This Overkill?" Moment

The friend in my brain: "So... you already have a working map."

Me: "Yes."

Friend: "And users can click states and navigate."

Me: "Yes."

Friend: "Then why are you rebuilding it with Leaflet?"

Me: "..."

Me: "Because I can?"

Friend: sighs

The Truth:

  • Do I need zoom controls for state boundaries? No.
  • Do I need choropleth visualization? Not really.
  • Do I need hover tooltips? Tables work fine.

But here's the thing:

Side projects aren't about "need." They're about:

  • Learning new libraries (Leaflet)
  • Solving interesting problems (GeoJSON rendering)
  • Building portfolio pieces (impressive demos)
  • Having fun (the real reason)

If I only built what was "necessary," I'd never learn anything new.


The Implementation (Week-by-Week)

Hour 1: Basic Setup

  • Install leafletreact-leaflet
  • Create /map route
  • Fetch India GeoJSON
  • Render basic map
  • Result: Blue static map, no interactions

Hour 2: Data Integration

  • Connect to PostgreSQL
  • Aggregate state-level metrics
  • Implement color scale
  • Add click handlers
  • Result: Colored map with navigation

Hour 3: District Maps

  • Create /map/[stateCode] routes
  • Fetch state GeoJSON files
  • Connect district data
  • Add district cards below map
  • Result: Full drill-down experience

Hour 4: Polish

  • Add tooltips
  • Implement legend
  • Fullscreen mode
  • Mobile optimization
  • Result: Production-ready map

Next: Performance

  • GeoJSON caching
  • Dynamic imports (avoid SSR issues)
  • Loading skeletons
  • Error boundaries
  • Result: Fast, robust, user-friendly

The Challenges (Because Nothing Ever Works First Try)

Challenge 1: "window is not defined"

Error:

ReferenceError: window is not defined

Cause: Leaflet uses browser APIs. Next.js SSR doesn't have window.

Solution:

// Dynamic import with ssr: false
const Map = dynamic(() => import('./map'), {
  ssr: false,
  loading: () => <MapSkeleton />
});

Challenge 2: GeoJSON State Names Mismatch

GeoJSON: "Andhra Pradesh"

Database: "ANDHRA PRADESH"

Result: States not matching, no data displayed

Solution:

function normalizeStateName(name: string): string {
  return name.toUpperCase().trim()
    .replace(/&/g, 'AND')
    .replace(/\s+/g, ' ');
}

Challenge 3: Mobile Tooltips Not Working

Problem: Hover doesn't work on touch devices

Solution: Detect touch device, use tap instead of hover

const isTouchDevice = 'ontouchstart' in window;
const eventType = isTouchDevice ? 'click' : 'mouseover';

The Performance Numbers

GeoJSON File Sizes:

  • All India: 450KB (simplified)
  • Maharashtra: 120KB
  • Uttar Pradesh: 180KB (most districts)

Load Times:

  • Initial map load: ~1.5s (including GeoJSON fetch)
  • State map load: ~800ms
  • Interactions: 60fps (smooth)

Optimization Techniques:

  1. GeoJSON Caching: Keep in memory after first load
  2. Dynamic Imports: Only load Leaflet on map pages
  3. Coordinate Simplification: Use simplified GeoJSON (10% detail)
  4. React Query: Cache DB queries for 5 minutes

The Open Source Lesson

Before this project, I thought:

  • "I need to build everything from scratch to learn"
  • "Using others' work is 'cheating'"
  • "Real developers don't use libraries"

Now I know:

  • Standing on shoulders of giants is smart, not lazy
  • Open source accelerates learning exponentially
  • Community contributions make ambitious projects possible

What @udit-001's repositories taught me:

  1. Someone has solved your problem. Just search.
  2. Clean data is worth gold. GeoJSON files saved me weeks.
  3. Code examples teach patterns. I learned Leaflet best practices.
  4. Give credit. Always acknowledge sources.
  5. Contribute back. I'll add MGNREGA-specific examples to my repo.

The GitHub Philosophy:

See a problem → Search GitHub → Find solution
↓
Learn from it → Build on it → Share your version
↓
Others find YOUR work → Cycle repeats

This is how the web gets built. One open-source project at a time.


The Unnecessary Features I'm Adding Next

Because why stop now?

  1. Metric Toggle
    • Switch between: Expenditure | Households | Person Days | Works Completed
    • Real-time color scale updates
  2. Time Slider
    • Slide through months: April 2024 → October 2025
    • Animate employment trends
  3. Comparison Mode
    • Select 2 states
    • Side-by-side maps
    • Highlight differences
  4. Export Options
    • Download map as PNG
    • Export data as CSV
    • Generate PDF report

Are these necessary? Not at all.

Will they be cool? Absolutely.

Will I build them? You know it.


The Takeaway

Building this map taught me:

  • Overkill is underrated
  • Open source is a superpower
  • Learning happens when you go beyond "good enough"
  • Side projects are for experimentation, not efficiency

If I only built what was "necessary":

  • I'd never learn Leaflet
  • I'd never discover india-maps-data
  • I'd never push my skills further
  • This blog post wouldn't exist

The "unnecessary" features are where the real learning happens.


Shoutout & Gratitude

Huge thanks to @udit-001:

  • india-maps-data: Clean GeoJSON files for all Indian states
  • india-maps: UI reference and implementation patterns
  • Saving me from weeks of map data processing hell

Check out his work:

This is the power of open source. One person's weekend project becomes the foundation for someone else's production app.

The Reality Check

Did I Use LLMs?

Yes. A lot.

  • Initial code scaffolding
  • Prisma schema design help
  • Translation assistance
  • Debugging weird TypeScript errors
  • CSS Grid layout calculations

Did I "Vibe-Code" Everything?

Hell no.

Here's the truth:

  • LLMs are tools, not magic wands
  • You need to understand your stack
  • You need to review all generated code
  • You need to test everything
  • You need to debug when things break (and they will)

What LLMs CAN'T do:

  • Understand your project's architecture
  • Make design decisions for you
  • Debug production issues (without context)
  • Optimize performance
  • Handle edge cases

What I DID:

  • Designed database schema
  • Architected sync system
  • Chose tech stack
  • Made UX decisions
  • Debugged all issues
  • Wrote documentation
  • Tested on real devices

LLMs helped me code faster. They didn't code FOR me.

The Numbers (Final)

Data Coverage

  • States: 36 states and UTs
  • Districts: 740 (Maharashtra: 34 complete)
  • Records: 646+ monthly metrics (Maharashtra)
  • Time Range: April 2024 - October 2025
  • Data Points: 35+ metrics per record

Technical Metrics

  • Lines of Code: ~15,000 (excluding dependencies)
  • Components: 40+ Shadcn UI components
  • API Endpoints: 6 REST APIs
  • Languages: 9 (3 production, 6 beta)
  • Translation Keys: 225+ total
  • Bundle Size: 150KB gzipped (first load)
  • Build Time: ~90 seconds
  • TypeScript: 100% coverage

Performance

  • Lighthouse Score: 90+ (target)
  • First Contentful Paint: < 1.5s
  • Time to Interactive: < 3s
  • API Response: < 500ms (uncached)
  • Daily Sync: 15-60 minutes (automated)

What I Learned

1. Assignments Can Evolve

Don't limit yourself to the brief. If you see potential, explore it.

2. Free > Paid (When Possible)

GitHub Actions > Vercel Cron Jobs (for my use case)

3. Simple > Complex (When It Works)

CSS Grid > 210KB SVG Map (for performance)

4. Automation > Manual Labor

15-60 min automated sync > Manual CSV updates forever

5. LLMs Are Tools, Not Gods

Use them to speed up, not replace thinking.

6. Real Users Have Real Needs

Voice input, multi-language support, mobile optimization — these aren't "nice-to-haves".

What's Next?

Short-term

  • ✅ Complete Maharashtra (Done)
  • ⏳ Optimize SVG map (still in repo, waiting for redemption)
  • ⏳ Add 5 more states (UP, Bihar, Karnataka, TN, Rajasthan)
  • ⏳ District detail pages with charts
  • ⏳ PWA support (offline mode)

Mid-term

  • ⏳ All 36 states/UTs
  • ⏳ ML-based insights
  • ⏳ Export data (CSV, PDF)
  • ⏳ Mobile app (React Native)

Long-term

  • ⏳ Real-time updates
  • ⏳ AI chatbot
  • ⏳ Predictive analytics
  • ⏳ Government partnership (fingers crossed)

Try It Yourself

Live Demo: MGNREGA INDIA

GitHub: github.com/certainlyMohneeesh/mnrega-mah

Want to contribute?

  • Optimize the SVG map (please, I'm begging)
  • Add more language translations
  • Report bugs
  • Suggest features

Final Thoughts

This started as a 2-week assignment. It's been 3+ months.

I fought:

  • Infinite fetch loops
  • Vercel timeouts
  • Laggy SVG maps
  • CSV hell
  • Daily data sync nightmares

I learned:

  • GitHub Actions are underrated
  • CSS Grid is underrated
  • Free tiers are your friend
  • LLMs are powerful tools (not magic)
  • Real users need accessible UIs

Would I do it again?

Yes. In a heartbeat.

Because assignments that turn into real projects? Those are the ones that matter.

Let's Connect

GitHub: @certainlyMohneeesh

LinkedIn: linkedin.com/in/mohneeesh

Email: certainlymohneesh@gmail.com


P.S. If you know how to make a 19MB GeoJSON SVG map NOT lag on mobile, seriously, DM me. That code is still sitting in interactive-india-map-v3.tsx, mocking me. 😅


⭐ Star on GitHub • 🐛 Report Bug • 💡 Request Feature

0 viewsSign in to like

Comments

No comments yet.

    Sign in to comment.