Making GitHub Wrapped

The tech stack we used to reach 8k users in two weeks

Ted Spare's avatar
Ted Spare

Dec 14, 2021

Summary page of GitHub Wrapped.

Summary page of GitHub Wrapped.

We built and launched a side project, called GitHub Wrapped, in two weeks. One week to build, another to polish. It's based on Spotify Wrapped and designed to be shared. Here, we'll talk about the technical component of the project with the hope that you can take something from it.

It's rare we have the chance to work on greenfield projects as software builders. Usually, our code must fit within the constraints of prior decisions. But does a clean slate really exist? Starting a full-stack project with create-react-app or a cloned repo constrains us to the decisions of (🤞) wiser developers. Even a blank Notion doc puts us in Notion's (very flexible) box. From there, it's up to us whether to make something others can use.

We asked the question: what stack of today's best-of-breed web dev tools will get us to users fastest?

If you're a seasoned full-stack developer, feel free to skip this cheesy analogy. If not, think about the tech stack as an open-face sandwich:

  • Sauce: styling. Colours, fonts, spacing, and animation. You could go without it but it makes the experience much richer.
  • Toppings: layout. They determine how the sandwich looks and feels.
  • Patty: business logic. It's the core of the experience.
  • Bread: database. It supports everything and is rarely seen.
  • Plate: infrastructure. It's not actually part of the stack; we don't touch it (assuming it's in the cloud).

With that in mind, let's step through the decision-making at each layer of the stack.

A component with complex styling, layout, and data processing.

A component with complex styling, layout, and data processing.

Appearance 🌶️

Browsers only speak CSS (Cascading Style Sheets) when rendering a website. CSS specifies which style should be applied to which element. Sass extends CSS (without changing syntax) with programming concepts like variables, nesting, and mixins (methods).

Tailwind offers almost all CSS functionality in a more intuitive syntax. It's applied to elements where they live, as classes. Its newest iteration, Tailwind v3.0, compiles faster to less CSS than anything else out there. That, alone, makes it our top choice.

For common visual elements, we used Radix pre-built components. They offer accessibility and keyboard navigation out of the box.

Layout 🥬🍅

Again, browsers only speak HTML when determining where to render what. HTML specifies a visual hierarchy of elements. If that hierarchy needs to adapt or react to a user's actions, it gets complicated. Traditionally, jQuery would be used to find and control elements from programming-land in JavaScript. It's so open-ended that it can lead to unmaintainable, spaghetti code. Some guardrails would help. That's what Angular, Vue, and React offer. React is the most popular and arguably the most flexible, so it's ironic that it's increasingly accompanied by Next.js.

Next.js makes React more opinionated to abstract away bundler details. It also adds futuristic features like rendering pages before we even click into them. Next uses a different compiler from almost every other framework, meaning it can hot-reload code changes in milliseconds.

Logic 🥓

In the previous section, we mentioned Next.js-flavoured React in the context of layout. It can also handle fetching and modifying data by talking to APIs. React is component-based: developers make custom lego pieces, each of which can modify and render data. That said, sometimes data needs to be modified in a way that would run a user's fan or leak a secret key.

For these cases, an API is needed. An API is just a Linux computer in a datacenter somewhere with specific channels of communication to the outside world. Sound complicated? To us too. We've found the fastest way to deploy reusable, custom logic is Express-flavoured Node.js on Railway. This project was entirely client-side but Next.js's API routes would have done the job too.

Data 🍞

When one user wants many others to access something, it's easier to store it in one place than to send it to everyone every time it's updated. Enter databases. They're Excel sheets without (or with) a UI. The most popular SQL flavours are MySQL and open-source PostgreSQL. We chose to go with the newer Supabase, which offers an intuitive DX (Developer eXperience), authentication with various providers (GitHub in our case), and row-level security.

Deployment

To extend the analogy, deployment would be the waiter carrying the sandwich. Not part of the stack but necessary to get the app in front of users. In brief, a web app is accessed by a domain name (like example.com). A domain name is nothing but a human-readable key to an IP address (93.184.216.34 in this case — see DNSMap). That IP address points to a computer connected to the internet. In practice, Vercel hides these details and deploys every time we push code to the connected GitHub repo.

To wrap it up 🥪

We can think of a web app as a hierarchy of technologies in increasing distance from the end user. Traditionally, this stack looks something like CSS (styling) on HTML (layout) with JavaScript (logic) fetching from Node.js (API), storing data in PostgreSQL (database).

Each layer of the stack has been evolving since day one, and it's our belief that today's fastest stack is Tailwind (styling) on Next.js (layout and logic) fetching from Supabase (API and database). Offer it to anyone on Earth in less than a minute by deploying on Vercel. Here's how I visualize it:

Conceptual diagram of the tech stack behind Wrapped.run.

Conceptual diagram of the tech stack behind Wrapped.run.

We hope you gain something from this project. What's unclear? What could have been done better?

Follow us on Twitter to let us know!