krishna@
~/projects
liveproduct · 2023 →

Joishi

Vedic astrology, actually computed.

Joishi — preview
01 // overview

Nepal-first calendar and Vedic astrology platform — public web app, iOS and Android app, astrologer portal, and admin console, all over a computation engine built from scratch. I led the project and owned the backend, the engine and the infrastructure alone: four codebases, 1.2M+ lines, zero-downtime deploys, shipping since 2023.

02 // the.story

the problem

Nepal runs on two calendars. Officially, legally and socially the country uses Bikram Sambat — a lunisolar calendar whose month lengths are not fixed, vary year to year, and cannot be derived from a formula. Every date in Nepali life is BS: your citizenship, your school certificate, the deadline on a government form, the day your family holds a shraddha. Meanwhile every computer, every API and every database on earth speaks Gregorian.

Bridging those two is only the entry fee. What people actually want is panchang — the five-limbed almanac that says which tithi it is, which nakshatra the moon sits in, when Rahu Kaal falls today and therefore when not to begin something that matters. Weddings, business openings, travel, naming ceremonies, the timing of a funeral rite: a great many real decisions in this country are planned around it.

The software serving that need was, charitably, not good. Static tables typed in by hand and copied between sites, wrong a few years out. Values that disagree between apps because nobody states which ayanamsa or house system they used. No source of truth and no way to check the arithmetic. For a domain whose entire product promise is precision, remarkably little was actually being computed.

Joishi is the attempt to compute it properly.

what it is

Four surfaces over one platform:

  • A public web app — daily panchang, the BS/AD calendar with festivals and public holidays, rashifal, date conversion, muhurta lookup, and live gold, silver and foreign-exchange rates.
  • A mobile app on iOS and Android — all of the above, plus home-screen widgets, notifications for the timings you care about, and a set of everyday Nepali utilities.
  • An astrologer portal — the professional side, where practitioners work with charts, consultations and client records.
  • An admin console — content, festivals, moderation, billing, notifications, queues, and the operational dashboards behind all of it.

Together: four codebases and something north of 1.2 million lines, built and shipped since 2023.

my role

I led the project. In practice that was four different jobs, and they are worth separating because they asked for different things.

I owned the backend and the computation engine alone. Architecture, data model, every module, deploys, on-call. Nobody else has committed meaningfully to that repository in three years. By a wide margin it is the largest single thing I have built.

I built the admin console — effectively solo, and it is the larger of the two web surfaces.

I co-built the public web app with a frontend developer, a roughly even split of delivery, with the architecture, the shared conventions and the review mine.

I was the largest contributor to the mobile app, working alongside a mobile developer who owned day-to-day feature delivery there.

I own the infrastructure and every deployment. There is no separate DevOps person on this project — the servers, the pipeline, the release process and the 3am pager are mine.

Those two specialists owned their surfaces. Everything else — backend, engine, admin, infrastructure, release, and the technical direction of the product — I handled.

the hard part: making astrology a computation, not a lookup table

The domain logic started where this always starts: inside the application, next to the HTTP handlers, growing. That works until the day it obviously doesn't. The calculations are the product, they are the hardest thing in the codebase to get right, and they had become tangled up with framework decorators, the ORM and environment config.

So I pulled the whole thing out into a set of internal packages under one rule, enforced by the build: the engine does not know it lives in a web application. No framework imports, no ORM, no environment variables, no decorators — plain classes, dependencies passed through constructors. The application composes them; the packages never reach back.

That constraint bought a great deal:

  • The core is browser-safe. Real astronomy needs the Swiss Ephemeris, a native binding and therefore Node-only. Rather than let that infect everything, the native backend sits behind its own entry point and the core defines an interface a host can implement instead. A browser can run the light parts with its own astronomical backend.
  • The boundaries are checked in CI, not in code review. One script fails the build on a circular dependency between packages; another asserts the browser-safe entry never pulls in a native or node: import. Architecture that isn't enforced decays. This one can't.
  • The engine tests without infrastructure — no database, no cache, no network, no framework container. The most correctness-critical code in the product therefore has the fastest and most trustworthy test suite in it.
  • Interpretation is data, not code — frozen, tree-shakeable lookup tables in four languages, kept separate from the mathematics that selects them.

The packages cover natal and divisional charts, the dasha systems, strength calculations, yogas and doshas, transits, annual charts, prashna and KP, compatibility matching, numerology, and the panchang and muhurta layer the whole calendar rests on. Roughly 200,000 lines of it. They stay private — this is the part of the product that isn't commodity.

the rest of the platform

The engine is the interesting half. The other half is a large product with ordinary large-product problems, done carefully:

  • Around fifty backend modules behind some 450 documented endpoints, with generated OpenAPI so neither client team ever had to guess a shape.
  • Four languages everywhere — English, Nepali, Hindi and Sanskrit — designed in from the start rather than retrofitted. The content model, the interpretation text and both clients are multilingual by construction. Shipping Sanskrit as a locale is not a problem most codebases have to solve.
  • Scheduled and queued work for everything that has to survive a restart: daily almanac generation, rate refreshes, notification fan-out, long computations. Durable queues, never in-process timers.
  • Real-time updates over WebSocket where they earn their keep.
  • Live market data — gold, silver and foreign exchange, normalised across sources and regions.
  • Payments, subscriptions, credits and coupons, because the professional side is a business.
  • Push, media storage, audit logging, moderation, health and status endpoints — the unglamorous surface area that decides whether a product survives contact with real users.

running it in production

Production is three-tier: nginx at the edge terminating TLS and reverse-proxying, a stateless application tier in hardened containers, and managed data services behind it — PostgreSQL with a tuned connection pool and statement timeouts, Redis for cache, sessions and durable queues, and S3-compatible object storage for user media and generated files. The application tier holds no state, so it scales sideways without sticky-session gymnastics.

Deploys are zero-downtime by design, not by hope. The app runs as two replicas behind nginx with passive health checks, and a release rolls them in sequence: take one out, upgrade it, gate on its health endpoint, and only then touch the other — so a healthy instance is always serving. Containers drain in-flight connections on shutdown, and the stop grace period is deliberately longer than the drain window so nothing is ever killed mid-request.

The pipeline around that is the part I am most pleased with:

  • Build first, switch last. Images build and migrations run before anything is swapped. A failed build, a failed migration or a failed health check leaves the previous version serving, untouched.
  • Automatic rollback. If a release fails its health gate, the pipeline returns each replica to the previous image tag by itself. I find out from a notification, not from users.
  • Staging runs the identical pipeline. Staging deploys automatically on release; production is a deliberate manual promotion of the exact same artefact. Nothing reaches production that hasn't already run on identical machinery.
  • Releases are automated from conventional commits — versioning and changelog included — so shipping is a merge, not a ceremony.
  • Containers are hardened by default: immutable read-only root filesystem, dropped capabilities, no privileged access, writable only where something genuinely needs to write.
  • Observability that answers questions rather than decorating a wall: error and performance tracing with profiling, structured logs, health and readiness endpoints, and a public status page.

Rate limiting, security headers, correct client-IP resolution behind the CDN, encrypted secrets with versioned keys so they can actually be rotated, and multi-factor and passkey-capable auth are all part of the same surface. Automated weekly dependency updates keep it from rotting.

The result is the only thing I really care about: I can ship a backend change in the middle of the day, with people using the site, and nobody notices anything except that the bug is gone.

leading it

Two specialists worked alongside me — one on the web front end, one on mobile — plus a rotating cast on smaller pieces. What I actually did to keep four codebases coherent:

Contracts before code. Every feature began as an API shape agreed in review. Both clients mocked against the spec while I built the real endpoint, so nobody sat blocked on me — which matters enormously when you are the only person who can unblock them.

One set of conventions across all four repositories. Same linting, same commit rules, same folder grammar, same release pipeline. A developer moving between the admin console and the public app relearns nothing. That is the only reason one person can meaningfully review all four.

Review with reasons. "Change this to that because" — never a rubber stamp, never a diktat. Both specialists got measurably faster over the project, which is the only measure of mentoring I trust.

Making myself unnecessary for the boring cases. I was the escalation path for everything at the start. I deliberately taught each side to read the logs and the system state so they could triage their own surface, which bought my time back for the things only I could do.

Documentation as a leadership tool. Architecture plans, decision records and pipeline write-ups live in the repository. When you are the single point of failure on the hardest component, the honest response is to write down what you know.

the stack, in shape

  • Backend: NestJS on Node, typed data access over PostgreSQL, Redis for cache and durable queues, WebSocket real-time, S3-compatible object storage, transactional mail, payments and push.
  • Engine: private TypeScript packages — framework-free, dual ESM/CJS, native ephemeris behind a Node-only boundary.
  • Web and admin: Next.js and React with typed state, four-locale internationalisation, and a permissions layer mirroring the backend's.
  • Mobile: Flutter on iOS and Android, offline-capable, with push and home-screen widgets.
  • Infrastructure: Docker containers behind nginx, managed PostgreSQL and Redis, S3-compatible storage, CI/CD on GitHub Actions with sequenced zero-downtime rollout and automatic rollback, error and performance monitoring throughout.

what stays with me

Correctness is something you can architect for. This product lives or dies on whether the numbers are right, and the best decision I made was refusing to let the calculations live inside a web framework. Everything good downstream — the fast tests, the enforced boundaries, being able to reason about a bug at all — followed from that one boundary.

Scale is a discipline problem before it is a technical one. 1.2 million lines across four repositories do not stay coherent because of clever code. They stay coherent because every repository shares the same conventions, every endpoint has a contract, and every architectural rule that matters is enforced by a script instead of by someone remembering.

Owning the deploy makes you a better engineer. When you are also the person who gets paged, you stop writing code that is merely correct on your laptop. Graceful shutdown, health endpoints, migrations that run safely against a live database, rollback that works without thinking — none of that is glamorous, and all of it is the difference between a product you can change and one you are afraid of.

The domain deserved the effort. People plan weddings and funerals around these timings. Shipping a rounded-off number because the real computation was inconvenient would have been the easy path, and the wrong one.

The web app is live at joishi.com, and the mobile app is on both stores.