Harshit Sharma
All work

Web AppQ1 2023Solo build

Chitter

A mini social app that lets users sign up, log in and post whatever is on their mind.

Chitter's landing page — headline, a Join Chitter Now button, and a phone mockup of the feed.

The build

A compact study in feed modelling — post, follow, unfollow, like.

Problem

A timeline looks trivial until you write the query. "Show me posts from people I follow, newest first" is one sentence in English and the single hardest read in a small social app — it spans two collections, it has no natural bound, and it runs on every page load.

The point of Chitter was to build exactly that, small enough to see clearly.

Constraints

  • MongoDB, no joins of consequence. The document model doesn't hand you a foreign key and an index the way a relational database does. Whatever the feed cost, it was going to be paid explicitly.
  • A study, not a product. Built to understand the modelling problem. That constraint is what kept it to four features instead of forty.

Approach

Three collections: users, posts, and the follow edge. The feed reads the edge first, then the posts:

js
// Fan-out on read: who do I follow, then what did they post.
const following = await Follow
  .find({ follower: userId })
  .distinct("following");

const feed = await Post
  .find({ author: { $in: following } })
  .sort({ createdAt: -1 })
  .limit(30)
  .lean();

Likes are their own small collection rather than an array on the post — an array mutated by every reader is a write-contention problem waiting to happen, and it makes "did I like this" a scan.

Tradeoffs

  • Fan-out on read over fan-out on write. Reading is two queries and always current; writing is one insert. The alternative — pushing each post into every follower's precomputed timeline — makes reads instant and writes expensive, and goes wrong the moment someone with many followers posts. At this size, reading was obviously right; at scale it obviously isn't.
  • Follows as their own collection over an array on the user. More documents, but unbounded growth doesn't live inside a single document, and both directions of the relationship stay queryable.
  • Server-rendered EJS. Consistent with everything else I'd built, at the cost of a timeline that reloads instead of updating in place.

Outcome

Four features, working: post, follow, unfollow, like. The value wasn't the app — it was learning where a document database makes you pay for what a relational one gives away, and being able to say specifically which query it is.

Decisions

  • $in over an aggregation pipeline with $lookup. Chosen because two simple queries are easier to reason about and index than one clever one. Against it: two round trips, and a $in list that grows with the follow count.
  • A separate likes collection over a counter on the post. Chosen so "has this user liked this" is an indexed lookup rather than an array scan. Against it: showing a like count now needs a count query or a denormalised field that has to be kept honest.
  • Not building notifications. Chosen to keep the study on the feed. Against it: notifications are where the interesting write-amplification problems live, so the project stops just short of the harder half.

Stack

  • Node.js
  • Express.js
  • MongoDB
  • EJS
  • JavaScript
  • Fomantic UI
  • The Lecture Lense workspace — a lecture playing above a chaptered time rail, the corrected transcript on the left, and a chat answer on the right with a timestamp citation.

    Lecture Lense

    Turns any recording into searchable, timestamp-cited notes.

  • A live Heckfree profile — avatar and the handle meharshit above seven stacked link rows, on a peach-to-purple gradient.

    Heckfree

    Users get a public profile that showcases all of their links in one place.

  • The Coscholars landing page — "Learn From The Best Tutors Worldwide" above a course search field.

    Coscholars

    An Ed-Tech platform built end to end during my internship at Coscholars.

esc

Pages

Home/
Work/work
Blog/blog
About/about
Uses/uses
Contact/contact
Bucket list/bucket-list
Guestbook/guestbook
Résumé/resume
Privacy/privacy
Terms/terms

Projects

Lecture LenseQ3 2026
ChitterQ1 2023
HeckfreeQ3 2022
CoscholarsQ3 2022
JsswireQ2 2022
JssconnectQ1 2022
QuizTownQ4 2021
IplheatQ2 2021
TechkartQ4 2020

Posts

Passport.js auth patterns I keep reaching for7 min read
How To Create A README For GitHub Profile3 min read
↑↓ navigate↵ openesc close