Case study — Reepro

A social platform for Nepal, built and shipped solo.

Reepro fuses LinkedIn — jobs, professional profiles, networking — with Instagram's reels and stories. It runs in production today, serving roughly 4,000–5,000 live users.

Users~5,000Live, in production
TeamOneSole engineer, end to end
Commits1,156July 2024 → present
AppsFourNX monorepo · mobile is API-only
MarketNepalBilingual, Devanagari-first
01 — The problem

Nepal had professional networking in one place and short-form social in another. Neither was built for the market.

Reepro answers that by putting both behaviours on a single social graph: one user model that carries a professional identity and a creator identity at once. Job seekers, professionals, and employers share the same posts, reels, stories, messaging, and calls — employers get job posting and recruiting layered on top rather than a separate product.

The interface had to work in Nepali script as well as English, which shaped the typography choices from the first component onward.

02 — Role & stack

Sole engineer,
every layer

Architecture, backend, frontend, DevOps and deployment, and production incident response. 1,156 commits across a four-app NX monorepo since July 2024.

Web appNext.js 15 App Router — the product, and the home of every web and mobile API route.
Dashboard APIExpress — dashboard-only endpoints, deliberately kept off the mobile contract.
Employer dashboardReact — recruiting, job posting, and admin moderation tooling.
Mobile APIThe React Native app itself isn't mine to claim — I built and maintain the API surface it runs on, on the same contract as web.
Frontend

Next.js 15 · React 18 · Chakra UI · Zustand · TanStack Query & Table · React Hook Form + Zod

Backend

Next.js API routes · Express · NextAuth v5

Data

PostgreSQL · Prisma · Redis · BullMQ

Media

FFmpeg · HLS.js adaptive streaming · AWS S3 · CloudFront

Realtime

Socket.io messaging · Agora RTC with token auth

Infra

Self-managed VPS · custom deploy scripts · Nginx

03 — Architecture

One boundary,
enforced everywhere

All web and mobile APIs live in a single Next.js app; Express serves the dashboard only. Holding that line kept the mobile app on one contract and left web iteration fast.

Video processing pipeline
01Chunked uploadClient uploads in parts; the request returns before any encoding starts.
02S3Source lands in object storage as the single input of record.
03BullMQ jobQueued on Redis; progress written back for the uploader to poll.
04FFmpeg transcodeH.264 / AAC, adaptive bitrate, capped at 1080p.
05HLS segmentsSegmented and packaged for adaptive playback.
06CloudFrontDelivered from CDN edge to the reels viewer.

Progress is tracked in Redis and surfaced back to the uploader. The transcoding worker runs as a dedicated standalone process, split out from the web app so a heavy encode can't touch request latency.

One API surface

All web and mobile APIs live in the Next.js app; Express is dashboard-only. That boundary keeps the mobile contract stable for whoever builds the client, while web iterates freely.

Role-aware single user model

One user carries both identities. Posts, reels, stories, messaging and calls are shared; employer-only job posting and recruiting sit on top rather than forking the product.

MCT profile scoring

A proprietary completeness index that scores a profile from resume, experience, education, skills, portfolio and certifications.

04 — Deep dive

Reels: moving the work off the upload

Uploads used to block on processing. Now a chunked upload lands in S3, a BullMQ job picks it up, and the user is free immediately — the reel appears when the worker finishes.

BeforeBlocking upload
AfterBackground processing
  • Explore and feed with infinite scroll and windowed rendering.
  • Admin moderation dashboard for reported and flagged reels.
  • "Stuck reel" bulk recovery tooling for jobs that die mid-pipeline.
  • Dedicated worker process, isolated from the web app.
05 — Production incidents

Six things that broke,
and why

Diagnosed and fixed in production, on a live user base, with no second engineer to hand the pager to.

01
Cross-region splitLatency

The production database and app server were sitting in different datacenter regions, adding latency to every single request.

Fix — Planned and executed a syd1 → blr1 migration with the old box held as rollback, then decommissioned it once the new region proved stable.

02
Connection pool exhaustionTwo outages

Prod, staging, and system processes shared an unbounded per-service connection budget against a 25-connection database.

Fix — Explicit connection_limit tuning per service, sized against the real budget rather than left to defaults.

03
Notifications going nowhereSilent failure

Next.js bundled a separate instance of the in-memory event bus per API route, so events published on one instance had no listeners on another. Every notification dropped silently in production.

Fix — Promoted the bus to a globalThis singleton so all routes publish and listen on the same instance.

04
Video calling downOutage

The Agora credential was exposed to the client through a NEXT_PUBLIC_ env var — the real root cause behind every call failing token generation with a 500.

Fix — Rotated the credential and moved token generation server-side.

05
Missing runtime dependencyLive 500s

Output file tracing missed zod, used only by a pages/api socket route, so the deployed bundle was incomplete.

Fix — Patched the break and added a deploy-time dependency top-up guard so future file-tracing gaps fail loudly instead of in production.

06
Secrets hygieneSecurity

A committed .env.production file and multiple copies of a Firebase Admin service-account key were tracked in the repository.

Fix — Untracked both and began credential rotation.

06 — Design system

Built from scratch, in two scripts

A custom Chakra UI theme with its own component variants — Button, Input, Modal, Tabs and the rest — plus a typography stack chosen for Devanagari support: Mukta and Tiro Devanagari Hindi alongside IBM Plex Mono.

IBM Plex Mono — 0123456789Mukta / Tiro Devanagari — नेपाली
07 — Constraints

The limits did the architecture work.

~5,000 live usersProduction stability was non-negotiable — auth, profile data, messaging and calls all had rules I maintained around them.
Single-engineer teamEvery architectural call traded speed of iteration against the blast radius of getting it wrong, with no one to catch it.
25-connection DB, 24GB-root VPSResource limits forced deliberate tuning instead of throwing hardware at the problem.
08 — What's next

Still shipping, still on call.