Announcing the release of Course Superlatives

It started as just favorite courses. It quickly spiraled out of control.

I’ve heard “What are your favorite courses?” twenty times this week, and figured it might be a question worth asking the whole school. So for the past few days, I’ve been working on exactly that—a project to compile the favorite courses of Yale.

This was incredibly timely, as I recently finished scraping a lot of data with the CourseTable API, iterating on many of the lessons I learned from JankTable with GraphQL (I also worked on rebuilding JankTable into ReviewTable).

The courses students choose can be life-changing and define their university experience. For a polished guide on this, I recommend Bluebooking for Happiness is the Bible for Yale Course Selection.

Structure Through Iteration And The Nature of Iterative Thinking

This project, more than anything, was an example of letting structure emerge organically. There are so many examples of “excess” design decisions that didn’t make it to the end of the project, but were necessary, in my opinion, for its completion. In this case, C can only be achieved through A->B->C, not A->C. The intermediary step is crucial for discovering the path to success.

From a User Interface Perspective

I went from TailwindCSS -> Google Sheets -> Quasar, and likely would have not arrived at Quasar had I lost myself in Google Sheets and realized its limitations.

  1. I initially started with SvelteKit and TailwindCSS, as I wanted to put this form under JankTable to reuse as much logic as possible.
  2. I realized that a a Google Sheet should be more than enough for what I needed to do, and that I was wasting my time making a user interface in TailwindCSS when Google Forms is more than enough.
  3. I try porting everything into a Google Form and Sheet with some limited usage of my SQL backend.
  4. I realized that Google Forms and Google Sheets actually has a big problem: they suck at rendering a lot of text at once. I thought that Google Sheets came with a powerful virtual scroller, but this is not true at all, as seen here in this spreadsheet of reviews where it struggles to render many rows at once. The Google Forms I was using couldn’t render all of the course options without getting immense lag. CleanShot 2023-04-25 at 20.53.01.jpg
  5. Google Sheets is not as versatile as I thought. It’s time to switch again!
  6. I decided I needed a dropdown component with virtual scrolling to be able to display all the options without slowing down your computer. It would be a pain to try to write this myself in SvelteKit and TailwindCSS, but Quasar has a bunch of pre-built components that do all this for me.
  7. I rewrote my user interface in Quasar, the ideal candidate, and fell in love with the ease of use. I turned to the Quasar framework, which offered sensible defaults and a smoother experience when building user interfaces. The framework’s Material UI design made it much easier to work with compared to JankTable or Tailwind, and the level of constraint in the documentation allowed me to create something practical and well-designed.

From a Database Perspective

I went from storing everything in SQL -> storing everything in a Google Sheet -> storing everything more jankily in SQL like a Google Sheet.

  1. I attempted to scrape everything from the CourseTable API and copy them into my own Supabase PostgreSQL database for maximum flexibility.
  2. I realized that I didn’t need to store user login information. Instead, I will just have them input their email at the form.
  3. Did I even need the PostgreSQL Database at all? Maybe I’ll just use a Google Sheet.
  4. I start making a bunch of scripts, scraping everything, using a bunch of random GraphQL queries to populate a Google sheet rather than storing any data in a database.
  5. Then I realize the Google Sheet was not enough, and I can’t call the GraphQL API client-side with Quasar because of CourseTable’s CORS policy. CourseTable was unable to fix the API to enable this even after approving my pull request to expand CORS.
  6. I go back to populating the PostgreSQL database and calling it client-side via Supabase’s client-side API.
  7. I populate the PostgreSQL table like a Google Sheet rather than a normalized table. This actually is super good, especially when using my script to copy Supabase Tables into a Google Sheet. I learn that It’s okay to store whole JSON objects in a row in PostgreSQL.
  8. I try caching so I don’t burn through network egress in Supabase with Storage and JSON. I accomplish this by selecting all course rows in Supabase, then clicking “Export to CSV”. I stored the result in Supabase Storage and serve that for the course dropdowns that list all courses.

From an Ideas Perspective

From an ideas point of view, this was also a mess. The project evolved from just favorite course to a superlative contest. As I invested more and more time into the project, I figured I might as well include more questions because it wasn’t that much more effort to add a few more questions. Brainstorming and a lot of ideas left me these:

  • Favorite courses of all time
  • Worst courses of all time
  • Most disappointing
  • Chillest (guttiest) courses
  • Best writing, science, humanities, social sciences, QR courses
  • Best professors of all time

At this point, I was torn between displaying all of the questions at once in a longer form, or a stepwise form. The stepwise form looked cleaner and could hold more questions, but I was worried users would click away if they could not see all of the questions at once.

This was a tradeoff of more questions per user or more users per questions, and ideally I hit somewhere in the middle of that continuum.

CleanShot 2023-04-22 at 00.58.57.jpg

CleanShot 2023-04-22 at 01.00.03.jpg

Key Takeaways

The journey of integrating with the CourseTable API and overcoming the challenges I encountered taught me several valuable lessons:

  1. Google Sheets may not be the best tool for rendering large amounts of text: It’s a lot harder to render stuff than it seems. Google Sheets struggled with rendering a large amount of text, making it less than ideal for this project.
  2. Quasar framework offers a more streamlined approach: When push came to shove and it was time to start rendering huge amounts of text in user interfaces, Quasar framework really had my back. It turns out that Quasar comes with really nice sensible defaults, especially in the framework and material UI design. I was extremely grateful for all the different ways in which it was easy to work with. Quasar is arguably easier to work with with AI because there is a set of narrow patterns in which there is an acceptable way you can build a UI and within the documentation. There isn’t extreme flexibility like Tailwind, and sometimes that level of constraint is a good thing and means that generative AI converges on something that is pretty good and pretty well-practiced.
  3. The Nature of Iterative Thinking This project was an example of expanding a bunch, but contracting and simplifying to actual truth. Jank is good, and I eventually arrived at a jank solution after many iterations.
  4. Don’t use a Google login, just trust your users to write their email Instead of using extremely complicated authentication methods, just have your users write their email at the top of the form. No authentication required.
  5. Pinia stores are great. They encapsulate information, and with toRefs, they are almost as good as Sveltekit stores. They are more opinionated and when used aggressively, your template has almost no Javascript at the bottom.

In conclusion, the process of integrating with the CourseTable API and overcoming the challenges I encountered allowed me to gain valuable insights into the strengths and weaknesses of different tools and frameworks. These lessons will undoubtedly serve me well in future projects. Finally, I sent an email to announce its release.

I’m going to make a pivot table of most frequent cited favorite courses, and their areas and skills

)