Skip to content

Hello there! I'm

Charan Mudiraj

Full-stack developerHyderabad, India

Experience

GrowMeOrganic

  1. Frontend Developer

    Full-time · Sep 2025 – present

    Has also worked on the Django backend since Jan 2026.

  2. Frontend Developer Intern

    Sep 2024 – Aug 2025

Tech used

  • React
  • React Router
  • Astro.js
  • Python
  • Django
  • REST APIs
  • PostgreSQL
  • AWS S3
  • Claude Code
  • Backend features in Django and PostgreSQL: reviewing UI designs, planning the architecture, writing DB migrations and REST APIs.
  • Led the project to build an internal tool that creates and maintains designs using Claude Code.
  • CSV upload and download that goes straight between the browser and S3, using presigned URLs.
  • Internal React tools for tracking revenue, subscriptions and users, built on the Django backend.
  • Pages, components and features for the main React app and the Astro.js marketing site.
  • A custom React Router dataStrategy that lets child loaders await their parent loader's promise (blog post).
  • Interviewed React intern candidates and co-led the intern team.

Wuri (Y Combinator W24)

  1. Software Developer Intern

    Jun – Jul 2024

Tech used

  • React
  • Firestore
  • Firebase Cloud Functions
  • Google Cloud Platform
  • MongoDB
  • The only full-stack developer at the startup.
  • Built the IFF24 submission site (React, Firebase), which got 900+ submissions.
  • Built the first company website from Figma designs.
  • Built a Firestore admin dashboard.
  • Wrote Firebase Cloud Functions and the Firebase-to-MongoDB migration.

What people I've worked with say

  • via Slack

    I really like your thought process and your approach towards the problem. It doesn't matter you work on frontend or backend, you're going to be a really good engineer

    Rudra Gupta(LinkedIn)Co-founder · GrowMeOrganic
  • via Slack

    On my fix for the dashboard's repeated Firestore reads

    Great work

    And in the last couple of weeks you are able to think about corner cases and failure scenarios, which will make this system quite stable.

    Your logic for adding last updated time in trigger function makes sense as it will take care of all the updates

    Akshay Megharaj(LinkedIn)Co-founder · Wuri
  • via Slack

    On the IFF24 submission site (form + landing page), built and shipped in my first week

    So congratulations on your first deployment

    Great work last week

    This was literally completed in one week only

    Akshay Megharaj(LinkedIn)Co-founder · Wuri

Problems I've solved

Two problems from my work, and how I approached them.

GrowMeOrganic · React Router

Child routes reading data their parent hadn't loaded yet

The problem

React Router runs the loaders of all matched routes at the same time. Some child loaders needed state that a parent loader sets, for example the signed-in user. On a fresh page load they read it before the parent's request had finished, and got null.

What made it tricky

The obvious fix is to run the loaders one after another. That works, but it turns every page load into a waterfall: the deeper the route, the longer the wait, even for loaders that don't depend on anything.

What I did

  • Each route lists the loaders it depends on in its handle, e.g. dependencies: [rootLoader].
  • A custom dataStrategy still starts every loader at once, but passes each dependent loader a promise for the parent loaders it listed.
  • The child loader awaits that promise before reading the shared state. Loaders without dependencies never wait, and on client-side navigations where the parent doesn't run again, there's nothing to wait for.

Result

Child loaders always see the data they need, and pages still load their data in parallel. Each route declares its own dependencies instead of having them wired into the loaders.
Read the full write-up

All loaders start together. The page loader reads the user before the root loader has stored it, and gets null.

waiting for the parentall data readyIllustrative timings, not measurements.

// The route declares what it depends on
{ path: "user-profile/", loader: userProfileLoader,
  handle: { dependencies: [rootLoader] } }

// dataStrategy: start every loader, but hand
// dependents their parents' promises
promises.push(
  match.resolve((callLoader) =>
    callLoader(Promise.all(parentPromises)))
);

// The child loader waits only for what it needs
async function userProfileLoader(args, parentsLoaded) {
  await parentsLoaded;
  const user = store.get(userAtom);
  // ...
}
Simplified from the blog post

Wuri (Y Combinator W24) · Firestore, Cloud Functions

A dashboard that kept re-reading data that never changed

The problem

Wuri's internal dashboard listed every IFF24 form submission from Firestore, grouped by day. Each time it opened, it read every past day's submissions again, even though older days almost never change. As the days added up, the reads went past Firebase's free limit and started costing money. The co-founder flagged the cost, and I worked out the fix on my own.

What made it tricky

Caching the old days would cut the reads, but a plain cache shows stale data as soon as an older submission is edited. The dashboard needed a cheap way to tell which days had actually changed, without reading their submissions.

What I did

  • Gave each day's document (the parent of that day's iff24-submissions collection) a lastModifiedTimestamp field.
  • Wrote a Cloud Function trigger that sets it to the current time whenever a submission in that day's collection is created or updated.
  • The dashboard keeps each day's submissions in localStorage along with the timestamp it saw. On open it reads only the day documents, and fetches a day's submissions again only if its timestamp is newer than the cached one.

Result

Unnecessary reads dropped significantly, and edits to older submissions still show up, so the dashboard never shows stale data.
What Wuri's co-founder said about it
  1. A submission is created or updated
  2. Cloud Function trigger runs
  3. That day's lastModifiedTimestamp is set to now

When the dashboard opens (days in July 2024)

Without the cache

With the cache

Without the cache, every day's submissions are read again each time. With it, the 16th had an edited submission and the 23rd got new ones, so only those two days are fetched; the rest come from localStorage.

fetched from Firestoreread from localStorageIllustration, not measured numbers.

Technologies I've worked on

Click an icon to filter the projects below.

Projects

Chat App
(Whatsapp Clone)
Get JSON
(Web Scraper)
Amazon Clone

Writing

From my blog on Hashnode.