We’ve all been there. You’re deep in the zone, debugging a race condition or refactoring a messy component. You code for 8 hours straight. You close your laptop, exhausted but proud.
The next morning, you check your GitHub profile and see... a gray square.
You forgot to push. Your 100-day streak is gone. 📉
I built DailyDiff to solve this. But I didn't want to build just another "fake commit bot" that spams update readme 50 times a day. I wanted to build a productivity companion that respects the spirit of open source while protecting your consistency.
Here is a deep dive into how I built a full-stack, timezone-aware automation tool using React, Node.js, Supabase, and the GitHub GraphQL API.
💡 The Core Concept: Ethical Automation
Most streak savers are simple scripts that push empty commits. DailyDiff is different. It offers Content Strategies:
- Learning Logs: It can append what you learned that day to a repo.
- Micro-Tasks: Stuck in "coder's block"? It suggests real tasks (like "Refactor variable names" or "Add JSDoc") to unblock you.
- Timezone Intelligence: It knows your day isn't over until it's midnight in your city, not UTC.
🛠 The Tech Stack
I chose a distributed architecture to keep the frontend fast and the backend persistent.
- Frontend: React (Vite) + Tailwind CSS (deployed on Vercel).
- Backend: Node.js + Express (deployed on Render).
- Database: Supabase (PostgreSQL) for storing user schedules and OAuth tokens.
-
Automation:
node-cronfor background job scheduling. - Auth: GitHub OAuth 2.0.
🧠 Technical Deep Dive: The "Minor" Details That Matter
Building a CRUD app is easy. Building a system that runs reliably in the background on free-tier infrastructure is where it gets interesting. Here are the three biggest engineering challenges I solved.
1. The "Sleeping Server" Problem
I deployed the backend to Render's Free Tier. It’s great, but it has a catch: it spins down after 15 minutes of inactivity.
If my server is asleep, the Cron job responsible for checking commit schedules (cron.schedule('* * * * *')) dies.
The Solution: The Heartbeat Route
I added a lightweight health-check endpoint in Express:
// server/index.js
app.get('/', (req, res) => {
res.status(200).send('✅ DailyDiff Backend is Active');
});
Then, I set up an external monitor (using cron-job.org) to ping this route every 14 minutes. This keeps the Render instance "warm" 24/7 without costing a dime, ensuring user automations never miss a beat.
2. Solving GitHub API Rate Limits (The Hybrid Cache)
DailyDiff visualizes your contribution history using a beautiful heatmap. To render this, I fetch data using GitHub's GraphQL API.
The problem? fetching a year's worth of contribution data is expensive. During development (thanks to React Strict Mode double-invoking useEffect), I hit GitHub's 5,000 req/hour limit constantly.
The Solution: A Hybrid Caching Strategy
I implemented a two-layer cache to protect my API quota:
Layer 1: Client-Side (LocalStorage)
Before the frontend even talks to the server, it checkslocalStorage. If valid data exists (less than 1 hour old), it renders instantly. 0ms latency, 0 API calls.Layer 2: Server-Side (Disk Cache)
If the client cache misses, the request hits the server. The server checks a local JSON file (cache_contributions.json). Even though Render has an ephemeral filesystem, this disk cache survives brief restarts and prevents the "thundering herd" problem if multiple clients connect simultaneously.
// Code snippet from my caching logic
const cachedRaw = localStorage.getItem(CACHE_KEY);
if (cachedRaw) {
const { timestamp, data } = JSON.parse(cachedRaw);
const age = Date.now() - timestamp;
if (age < CACHE_DURATION) {
return data; // Return early!
}
}
3. Timezone-Aware Cron Jobs
Servers run on UTC. Users do not.
If a user in India (IST) sets a reminder for 11:00 PM, but my server is in Oregon (UTC-7), a standard cron job fails.
I couldn't just store 23:00 in the database. I had to store the user's Timezone (Asia/Kolkata) alongside the time.
In the backend cron loop, I don't check if ServerTime == UserTime. instead, I dynamically convert the server's now() into the user's local time before comparing:
// Inside the Cron Loop
const userTimeString = new Date().toLocaleTimeString('en-US', {
timeZone: job.timezone, // e.g., 'Asia/Kolkata'
hour: '2-digit',
minute: '2-digit',
hour12: false
});
if (userTimeString === job.schedule_time) {
// Fire the commit!
}
This ensures that 8:00 PM feels like 8:00 PM, no matter where the server is located.
🎨 UI/UX: The "GitHub Dark Mode" Aesthetic
I wanted the app to feel native to the developer ecosystem. I utilized Tailwind CSS to mirror GitHub's specific dark mode color palette (#0d1117 for background, #39d353 for success states).
I also implemented a React Portal for the tooltips on the activity graph. Because the graph is in a container with overflow-x: auto (for scrolling), a standard absolute tooltip would get clipped. Portaling it to document.body allows it to float freely above the UI.
🚀 Deployment & Security
Deploying a separated frontend and backend required strict CORS and Cookie configuration.
Since the frontend (vercel.app) and backend (onrender.com) are on different domains, I had to set the session cookies to SameSite: none and Secure: true.
app.use(cookieSession({
name: 'session',
keys: [process.env.SESSION_SECRET],
// Required for cross-site cookies between Vercel and Render
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
secure: process.env.NODE_ENV === 'production',
httpOnly: true
}));
🔮 What's Next?
DailyDiff is currently in v1. I'm planning to add:
- AI-Generated Commit Content: Using OpenAI to read the user's actual code changes and suggest commit messages.
- Team Leaderboards: Gamifying the streak experience.
🔗 Links
This project is fully open-source. I’d love for you to check out the code, fork it, or give it a star if you find it useful!
- Live Demo: https://daily-diff.vercel.app/
- GitHub Repo: https://github.com/KrDevanshu06/dailydiff Star it.🌟
Thanks for reading! Let me know in the comments—have you ever lost a 100+ day streak? 😅
Top comments (0)