DEV Community

Cover image for From Next.js to Rails then Elixir: My journey through React.js burnout
Daniel Bergholz
Daniel Bergholz

Posted on

From Next.js to Rails then Elixir: My journey through React.js burnout

I've been a web developer since 2019. I used React.js and React-based frameworks like Gatsby, Next, Remix, Astro, and Hydrogen. I've never been fully content with any of these tools, but, as a beginner who was deep into the JS ecosystem, all that I could hear from my peers was something along those lines: "This is the way, any other programming language is either slow or old".

This is the way

As a result, I got used to a huge amount of complexity: Multiple separate repositories, thousands of libraries and frameworks to achieve simple things, GraphQL, microservices, serverless, static site generation, incremental static regeneration, partial hydration, redux, redux-thunk, babel, webpack, react server components, server actions, etc. This list could go on for another 10 minutes.

Until one day I said ENOUGH IS ENOUGH! Let's take a look at the complete timeline of me slowly going mad. This will take a while, feel free to make some coffee before the long read!


The timeline of the burnout

Gatsby.js

I remember finishing my bootcamp and thinking: "Finally I'm able to build my portfolio!", and so I did. There was only one small problem, I wanted to index on Google, but using the good old create-react-app made this mission nearly impossible. Soon I learned about SEO and React's hydration cycle, which led me to the "solution" of this problem: Gatsby.js. The idea of static site generation was simply revolutionary for me back then, after all, nothing is faster than pre-rendered HTML files, right?

I decided to learn this new framework by reading the docs and let me tell you, this was NOT a fun experience. I have never heard of GraphQL before, and apparently, you needed it to generate all the static files (what the hell???). I asked some of my internet friends if having a hard time learning all of this overengineered crap was normal, and they replied with "Skill issue, try harder!". So I tried harder, and after finally learning it, I ported my personal website to Gatsby.

Try harder

Most of my pages were successfully indexed on Google, and for a couple of months, I was extremely satisfied with the result. Then another problem appeared: A LOT of my developer friends started saying "Gatsby is dead! Next was created to simplify static site generation and also provide server-side rendering".

Next.js

I took a quick glance over the Next documentation and immediately fell in love. I was able to do the same things as Gatsby without GraphQL and with a third of the code! Once again, I ported my portfolio to another framework: Next.

This time I truly had a wonderful experience. Deploying to Vercel was a breeze, the getStaticProps and getServerSideProps functions were simple, yet extremely powerful, I could choose the rendering style per page, a lot of flexibility in general.

Unfortunately, something I learned the hard way: In the JavaScript ecosystem, all the good things come to an end.

Remix

I remember extremely well when Remix was announced. Multiple tech influencers started publishing content about it (as always). However, back then I read on the home page that it did not support static site generation, just server-side rendering, so I thought "Wait a sec, all those years investing on the JAMstack are thrown away here? No way, this framework ain't gonna last". However, to my surprise, not only did Remix survive, but it was acquired by Shopify and emerged as a prominent competitor to Next.

After a couple of months had passed, I decided to give it a try. And once again, I was surprised, the main motto of Remix is to use the web fundamentals, and not an overly complex caching system like Next. So the mental model I needed in my head when coding in Remix was 10 times simpler: No global state manager, just use the URL, fewer client-side states, move all that logic to the server, and use cookies, going full stack without a REST API in the middle is super easy, just move your database queries to the loader function.

Leaving the Matrix

Leaving the Matrix

Then, out of nowhere, the truth was presented to me, and I took the red pill. Multiple questions started emerging in my head: Isn't Remix just like all the other "old and boring" frameworks like Rails, Laravel, and Django? We have been doing fullstack web development with server-side rendering for decades, but the JavaScript mafia decided collectively that this approach was trash, and moving everything to the client was the future. Did the same mafia decide that Rails was right all along? And doing all those over-engineered monstrosities with JS frameworks was not the right move? I started questioning everything. This "new" way of doing web development was a lot simpler and faster.

I'm DONE with Next and Vercel

I reached my tipping point with Next.js app router. Here is a comprehensive list of everything wrong that Vercel is pushing to Next:

  • What was once simple: The getStaticProps and getServerSideProps functions, now became complex and cumbersome. Currently, there is no specific place to add your API calls or database queries, you can write them wherever you want! We started mixing the business logic with UI once again, after making the same mistake with PHP multiple years ago. Do frontend developers not learn from the past? What happens if I delete a button? Does this break my user authentication flow because the database call was inside it? Your front end should be 100% trashable and replaceable. The competitive advantage you have against your competitors is the business logic, which should be completely isolated from the UI layer.

Horrible Next.js code

  • Next is now server first. Which doesn't sound that bad right? After all, this solves the SEO issue and shows fresh content to the user immediately. The problem is that most of the existing Next codebases relied on client-side libraries, like Styled Components and a couple of global state managers. What does this mean? With breaking changes like this happening constantly, your app becomes legacy software in a couple of weeks instead of years. More time is spent to keep all dependencies up to date rather than doing what matters: Shipping features.

  • Vercel hired multiple React core team members from Meta. This presents a serious conflict of interest because these engineers are now (allegedly) shipping features that are beneficial to Next instead of prioritizing the ones that could help all the React-based frameworks like Remix.

Vercel is corrupting React

I couldn't take it anymore. I said to myself: You know what? I am tired of re-learning the same framework over and over again, and I completely disagree with this new paradigm.

Not surprisingly, other content creators were going through a similar situation:

Burned out on Javascript - YouTube

What is going on in the Javascript / React community? Why is everyone burning out? I have... an idea.00:45 The hype cycle01:39 The Javascript hype cycle02:15...

favicon youtube.com

I'm Done With React - YouTube

After 8 years, I'm moving on from React for new web development projects.

favicon youtube.com

The Path to Enlightenment

TL;DR: I was extremely tired. After getting burned out from all the React tools, my journey for simpler web frameworks started. Here are the prerequisites I was looking for:

  • Batteries included
  • Convention over configuration
  • Good developer experience
  • Modern and performant frontend

My first instinct was to take a look at the top frameworks from the Stack Overflow Survey 2023. Immediately I cut out from the list anything JS, C# and Java related. I never had any desire to learn the last two, they look ugly and verbose. So the remaining options were: Laravel (PHP), Django (Python), Rails (Ruby), and Phoenix (Elixir).

Python is a language that I used during my Network Engineering degree and I had a very pleasant experience. Django seemed to follow the convention over configuration philosophy, but what turned me down from it ultimately was not having a good built-in tool to work on the front end. Most people on forums said they were using HTMX and Alpine, however, both are external dependencies that you need to install.

Giving up on Laravel was extremely hard because it has an amazing cost benefit, with hundreds of official packages to handle pretty much anything a startup might need, like hosting, authentication, stripe payments, etc. For the front end, they created inertia.js, a very simple and elegant way of keeping the high productivity and powers from Laravel while using React on the front end. To be 100% honest here, the only reason I didn't choose Laravel was because of PHP's syntax, it looks ugly as hell with a bunch of $ and -> everywhere.

Ruby on Rails

Ruby on Rails needs no introduction. It's the OG of web development frameworks, with the revolutionary "build a blog in 15 minutes", which is still impressive to this day. Before I start ranting about all of the problems I found, let's start with the good stuff.

Similar to Python, Ruby is that language that you can show to non-technical people and they will understand what the software is trying to do. It is by far the easiest to read and the most beautiful language I've ever seen. I quickly realized that writing visually pleasing code was a priority for the Rails team, and that was new to me.

Not to mention that Rails pretty much invented the "Batteries included" and "Convention over configuration" philosophies, so this wouldn't be a problem. Inside one single documentation, everything I needed for any type of web application was available.

On the frontend side, there is Hotwire, a very simple and lightweight approach to all the UX improvements provided by SPA frameworks. I've always been curious to test the limits of this library, it looks very promising.

Alright, so on paper Rails passed on all of the prerequisites I wanted on a framework. Let's try it! The first thing I tested locally was the rails scaffold command. And immediatly I was SHOCKED. One single command generates everything I need for a CRUD? No way!

Image description

On Node + React land, to achieve the same thing, I would need to manually write all the code (there are no generators here) and install a bunch of libraries like: Vite, prisma, express, react router, redux, redux-thunk, vitest, cypress, react testing library, zod, typescript, eslint, prettier, 1000 different plugins, and maybe even GraphQL or tRPC. Basically a package.json with 900 dependencies already.

After the initial shock from rails scaffold, I was shocked once again when I opened the code from the controller:

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render :new, status: :unprocessable_entity
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])

    if @article.update(article_params)
      redirect_to @article
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    @article = Article.find(params[:id])
    @article.destroy

    redirect_to root_path, status: :see_other
  end

  private
    def article_params
      params.require(:article).permit(:title, :body)
    end
end
Enter fullscreen mode Exit fullscreen mode

Is this all of the backend code? Just a couple of lines? That's impossible! This is so simple that it looks like a "low code" tool. It's simple, elegant, and extremely readable, which is something we rarely find in the JS land.

Okay, okay, you must be thinking right now: "This crazy react dev from the internet said he ended up using Elixir, so there must be some things wrong with ruby!". And you are right my anonymous friend, there were some things that annoyed me quite a lot, let's talk about them.

First, we need to address the elephant in the room: Moving from React + Typescript to a dynamically typed language is not easy. From the moment I started writing code and no intellisense or dropdown filled with code suggestions show up on my VScode, I felt blind and lost. This is a terrible feeling, I could make a typo on a function name and didn't realize it until the website is in production! I know we can write tests, but this is the type of mistake that I want to identify immediatly on the IDE, and not during tests or deployment.

Another thing I thought I would like, but ended up hating it: Too much magic. Inside a Typescript codebase, I can click on top of any class or function, go to the source and see how it's implemented. On Rails, where the hell do I do validation (for example)? Do I create a private function inside the controller? Is there a especific folder for this? NOPE, the correct place to do it is inside the model. Why? Because that's how it works, you either adopt the convention or have a hard time writing ruby code. I simply cannot develop an "intuition" on how everything works under the hood, I have to blindly trust that the maintainers did a good job at organizing everything.

And to finish my frustrations, I started writing frontend code. How do I create components? Partials. How do I define the prop types of this component? There is no way to do that, you need to open it up and visually look for all the variables inside it. How about doing some interactivity? Creating states? Well, there is Hotwire with Stimulus, but as you can see, you need to manually create your "re-render" function, it doesn't figure out a way to re-render the page automatically after changing a state like React.

// src/controllers/slideshow_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]

  initialize() {
    this.index = 0
    this.showCurrentSlide()
  }

  next() {
    this.index++
    this.showCurrentSlide()
  }

  previous() {
    this.index--
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index !== this.index
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Once Again I got frustrated. I got reeeeeeeally close to finding the perfect framework! What is the next framework on my list that I wanted to try if Rails failed? Elixir.

Elixir and Phoenix

I have to be honest, I was running low on patience. I tried multiple different ecosystems, and I was almost convinced to just stick with Ruby on Rails and give up on my quest to perfection. Until a video appeared on my YouTube recommended section:

My Favorite Language Isn't TypeScript - An Interview w/ Jose Valim - YouTube

I love Elixir and miss it dearly. Join me LIVE to talk with Jose all about it 🙏

favicon youtube.com

Hold on! Here we can see a React developer saying a bunch of nice things about functional programming, Elixir and Phoenix Live View. Maybe I should give it a try!

The first thing I did was open the documentation for Elixir and Phoenix, and I really enjoyed the fact that all packages are documented in the same way using Hex Docs, you just need to get used to one interface in order to learn new things.

Another good thing is that you can truly learn Elixir just reading the docs, no need for an expensive course! On every other ecosystem, I had to learn the language through a paid course and then learn the framework by reading the docs.

Then it was time to start writing code. Very quickly I understood that functional programming is very different from OOP. Let's do a small comparison:

// JS
const obj = {name: "daniel"}
obj.age = 25

// result: obj = {name: "daniel", age: 25}
Enter fullscreen mode Exit fullscreen mode
# Elixir
obj = %{name: "daniel"}
obj = Map.put(obj, :age, 25)

# result: obj = %{name: "daniel", age: 25}
Enter fullscreen mode Exit fullscreen mode

Or you could achieve the same thing with a simpler syntax using the pipe operator:

# Elixir with pipe operator
obj = %{name: "daniel"} |> Map.put(:age, 25)

# result: obj = %{name: "daniel", age: 25}
Enter fullscreen mode Exit fullscreen mode

Initially you might find it less readable and more complex, but I promise that over time it makes sense! Well, at least for me it did. As a React developer, I got used to seeing multiple functions everywhere, even front end components are functions! Not to mention the fact that creating a class is sometimes viewed as a code smell by the JavaScript mafia. My brain was already "shaped" for this new paradigm, it just felt natural to me. Since my Network Engineering degree in university, I had several classes about object oriented programming, but it never "clicked". I couldn't model complex problems into classes and objects. Using multiple functions to "mutate" a variable over time is how I model things in my mind.

How about the main framework? Is Phoenix batteries included? Convention over configuration? YES IT IS! To be honest, the ecosystem is not at the same level as Rails, but it's 95% there. Unless you need an ultra-specific feature, Phoenix got you covered.

I was almost sold on Elixir, 2 things were missing from my list: Good developer experience and modern/performant front end code.

José Valim announced he was experimenting with adding types to the language, but Elixir doesn't have them currently, so I got concerned. How do I get intellisense and autocomplete without types? Soon I discovered these features aren't necessarily related. After installing the ElixirLS extension on VScode I was surprised. It's possible to define a function inside a random module on a random folder, import it somewhere else, and get the intellisense and documentation for it! I have those benefits from statically typed languages without the hassle of writing types, simply amazing!

My Future with Elixir: set-theoretic types - The Elixir programming language

We announce and explore the possibilities for bringing set-theoretic types into Elixir.

favicon elixir-lang.org

My final concern on the frontend was addressed by Phoenix Live View. On the code side, this was the exact piece of the documentation's home page that convinced me:

Image description

You can define "props" to every component, and if the types mismatch, you get an error in your IDE, just like react! Impressive!

How about the UX? Is there a full page load whenever a user clicks on a link? Hell no! Live view establishes a WebSocket connection with the client, and then every page transition is just a content swap made through the Websocket, no new HTTP request is made. Also, all of the state is managed on the server side, which means that rich user experiences like Trello, which used to be very janky on the client side due to an excessive amount of javascript being loaded, are now super fast! Elixir handles all the complex state logic and sends the updated pieces of the page to the front end. Take a look at the full explanation here:

What Is: Phoenix LiveView - YouTube

Have you heard the good news about Phoenix LiveView?A big driver in Elixir and Phoenix interest, LiveView lets you get away with simplifying a lot of fronten...

favicon youtube.com

Since we are using WebSockets for builing the UI, creating "live" applications like Twitter takes only a couple of lines of code!

Build a real-time Twitter clone in 15 minutes with LiveView and Phoenix 1.5 - YouTube

With the Phoenix v1.5 release, learn how easy LiveView makesit to build interactive, real-time applications.

favicon youtube.com

Conclusion

It's safe to say that the "perfect tech stack" doesn't exist. The silver bullet that solves all the problems is an illusion that we create in our minds to keep searching and building the most optimized tool.

However, at an individual level, the perfect stack does exist. Because each developer has preferences, and you can easily find a tool that fits your criteria. If you had a similar journey to mine, perfection might be Elixir and Phoenix! So give it a try, maybe you'll love it as much as I do now.

If you reached the end of this blog post, you are awesome! Thank you so much for your time, and I hope I could bring some value into your career.

The end

Top comments (77)

Collapse
 
morphzg profile image
MorphZG

Definitelya high value post. Finaly something to read. Big respect. Thanks for sharing

Collapse
 
danielbergholz profile image
Daniel Bergholz

Thank you!

Collapse
 
lumsdendigital profile image
Ruaidhri Lumsden

Great post Daniel. It's interesting how similar or journeys have been so far - except that I've never been at all tempted by Ruby! I'm firmly in the Remix camp at the moment, and am quite happy with it at the moment, however you have inspired me to look into Elixir and Phoenix in greater detail.

I'm not in total agreement with all your points, I personally don't think that co-location is a bad thing.

You're right though, there's no such thing as the perfect tech stack, it's fun to keep looking though!

Collapse
 
danielbergholz profile image
Daniel Bergholz

I'm also in the Remix camp right now! I work primarily as a front-end developer, and my team uses Remix (Hydrogen to be more precise). And the developer experience from it is miles ahead of Next in my opinion. If Remix had batteries included (for the backend), I would still use it for my personal projects, but for my use cases I decided to move to Elixir + Phoenix

Collapse
 
lumsdendigital profile image
Ruaidhri Lumsden

It's good to hear something so positive about Hydrogen. I've got a potential e-commerce freelance job coming up and was considering it

Thread Thread
 
geni94 profile image
geni94

... all the great things for Hydrogen are coming from the 20 people who use it lol.

Collapse
 
lcmen profile image
Lucas Mendelowski

Thank you for sharing your thoughts!

Deploying Live view requires some additional caution, as restarting the server leads to state reload on the backend. There is built recovery solution for forms but custom components may require additional code. This is not a problem with Rails and Stimulus because state is stored in HTML.

Collapse
 
ankitsingh profile image
ANKIT SINGH • Edited

Finally, someone did a lot of hard work to write this. Daniel, I did the same. I started my journey in 2018 with React but slowly by the time 2020 Ruby on Rails took the tide, because of the market demand. Elixir is good, might try it someday.

Fun Fact this website is on Ruby on Rails. 😆

Collapse
 
peacechen profile image
Peace Chen

Frameworks are a double edged sword. They're convenient because they provide a lot boilerplate out of the box. However as you've noted, the big downside is that you're at the mercy of what the framework provides. If you stray from it, be prepared for much pain.

There's a place for frameworks. While they can hide some complexities, that can only go so far and is detrimental sometimes when debugging. There's no substitute for fundamental software engineering knowledge and experience.

Collapse
 
zaknarfen profile image
Renan Augusto Dembogurski

What about Blazor + .NET 8? I know you said no to C#, but the new version of Blazor blew me away. C# is improving drastically with every new version. The good thing about learning C# is that you can also make games with it, which is my hobby.

Collapse
 
danielbergholz profile image
Daniel Bergholz • Edited

To be honest, I was just too lazy to try C#, I never looked too deep into the language

Collapse
 
pppdns profile image
Denes Papp

you are misjudging C# my friend :) it's not the old and ugly Windows-related thing it was many years ago. It has become one of the most modern and sexy languages. The syntax is pretty much the same as Typescript. For example, writing a fully type safe hello world API is just 4 lines of code now.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();
Enter fullscreen mode Exit fullscreen mode

ASP.NET Core apps are now faster than Node.js apps and much much faster than PHP/Rails apps. Like 10x faster or more. They are pushing out new modern features quite often and the team has moved away from the clumsy old .NET platform to a new lightweight and easy to learn platform. It's like Node.js but easier, safer, and faster

Thread Thread
 
joshuaamaju profile image
Joshua Amaju

I can never get over MapGet in Microsoft related languages, looks so ugly

Collapse
 
kevgathuku profile image
Kevin Gathuku

Keeping up with the JS / React ecosystem is a huge challenge. Building a project and coming back to it after 2 months and nothing works anymore since there’s some new paradigm. Gave up on it entirely too! 😅

Collapse
 
danielbergholz profile image
Daniel Bergholz

Exactly my feeling as well! I can get a Ruby on Rails codebase from 5 years ago and it still works. But If I go back to a 1-month-old JS codebase, nothing works

Collapse
 
nicoka11 profile image
Nicoka11

After trying Next (production level at work), SvelteKit, and NuxtJs.
I would say the best to use is Nuxt for 3 simple reasons :

  • The docs are amazing and complete
  • The framework itself is really minimalist by default but extends quite a lot to suit your needs
  • It doesn't change as often as Svelte or even worse React (freaking server components)
Collapse
 
danielbergholz profile image
Daniel Bergholz

I’ve heard good things about Nuxt!

Collapse
 
marlonmarcello profile image
Marlon Ugocioni Marcello

Sounds like what you want is RedwoodJS. Heavily inspired by Ruby on Rails. Convention over configurations, generators, testing, component library, all configured and ready for ya with a single command. It's seriously underrated.

Collapse
 
danielbergholz profile image
Daniel Bergholz

Right now I would rather use Ruby on Rails. I hate that it’s mandatory to use GraphQL with Redwood. Just a lot of overengineering in general

Collapse
 
klebergueriero profile image
Kleber Nascimento Gueriero

OK, I actually didn't expect to see a post talking about Elixir coming from a frontend, specially talking about a journey coming from React.

It was, to say the least, interesting to know about your perspective in that journey.

Collapse
 
danielbergholz profile image
Daniel Bergholz

I think more React developers should give Elixir a try! It fits our mental model much better than a traditional OOP framework

Collapse
 
raythurnevoid profile image
Ray Thurne Void • Edited

I've been working with svelte kit in the past two years and you can get a functioning project with a single "npm create svelte" command.

And the code you have to write to create a new page/api is pretty much the same as in your Ruby's example.

Plus the components use the same language of the server with some small additions to the html side, this fix two language issue you face when not implementing the UI in JS, at some point you have to add some custom script in the page and you find yourself do deal with two languages.

The alternative is to use something like Dart or Reason that doesn't require you to write JS because they are compiled to JS code, but they both are a downgrade from the simplicity and the flexibility that comes with JS/TS.

Collapse
 
danielbergholz profile image
Daniel Bergholz

That's good to know!
At this point I'm so burned out from JS, that I just want to run away from it to be honest

Collapse
 
codewander profile image
codewander • Edited

Great post. I agree with most of it. I am ironically moving in the opposite direction. (I have used almost every language you can think of in production including haskell)

(Also, I might recommend golang + elm SPA).

I love elixir. I have been working with it full time (backend only) for almost two years. The only downside that I know of with liveview is the lack of ready accessible headless UI component library. I am also hoping either roc or gleam eventually beat elixir, but right now it is a top lang + framework.

I am moving in the opposite direction for job interviews only. If more early stage startups used elixir, I would drop js in a second. (If you are looking to use elixir professionally, that will be an uphill battle currently since there are few job openings and they usually focus on experienced elixir devs)

Collapse
 
harsh2909 profile image
Harsh Agarwal

Great article
For the problems that you faced while using Rails, you might want to look at Ruby-lsp extension. It adds Intellisense and other things to the editor that Rails have been lacking until now.
It's still a bit rought but much better than not using anything.

Collapse
 
danielbergholz profile image
Daniel Bergholz

I tried it, but to me, it wasn't enough to have a good developer experience. Elixir LS fits my needs better currently

Collapse
 
entrptaher profile image
Md Abu Taher

Being a JS developer, I have felt this burnout a few times. These frameworks are simply too much. Only a few frameworks and libraries remain critically unchanged over time, and provide backward compatibility.

Sometimes I have this urge to simply move to another stack, but get hit by the team I work with being a JS team.

Sometimes I wonder if I should just go with PHP or Python or even Go, or Rust. With this new WASM thing popping up.

Tech is getting crazy, from WASM to LLM to WebWorker, Kubernetes and whatnot. Its simply too much to take in.

Collapse
 
danielbergholz profile image
Daniel Bergholz

Yep, for sure. There is a lot of decision fatigue in the tech industry. Personally, I decided that I do not want to work professionally with JS long-term, so I'm planning to fully migrate to an Elixir job in the future. I'm burned out on React, Next, and all of these Frankenstein frameworks, it's just too much for me

Collapse
 
guledali profile image
guledali • Edited

Interesting post,

I'm about to build website for a client, initially I was looking on Next.js and Vercel but now I'm eyeing on Astro.build and vanilla javascript + web components

Not only is a lot simpler but support markdown with no additional npm install, it also ships no javascript.

Payload CMS is also good alternative, but I wanted something more simple.

If I was building an web application I probably would have used something in the vain of rails

Collapse
 
danielbergholz profile image
Daniel Bergholz

Very good approach! Astro for simple and static websites, and Rails for more complex backend stuff 🔥

Some comments may only be visible to logged-in visitors. Sign in to view all comments.