<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Bergholz</title>
    <description>The latest articles on DEV Community by Daniel Bergholz (@danielbergholz).</description>
    <link>https://dev.to/danielbergholz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1199300%2F236c734c-3824-43c2-86aa-072956e0628f.jpeg</url>
      <title>DEV Community: Daniel Bergholz</title>
      <link>https://dev.to/danielbergholz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielbergholz"/>
    <language>en</language>
    <item>
      <title>Announcing CourseShelf: Rotten Tomatoes for Online Learning</title>
      <dc:creator>Daniel Bergholz</dc:creator>
      <pubDate>Wed, 21 Jan 2026 22:10:59 +0000</pubDate>
      <link>https://dev.to/danielbergholz/announcing-courseshelf-rotten-tomatoes-for-online-learning-4h3k</link>
      <guid>https://dev.to/danielbergholz/announcing-courseshelf-rotten-tomatoes-for-online-learning-4h3k</guid>
      <description>&lt;p&gt;Back in 2019, I created &lt;a href="https://techschool.dev" rel="noopener noreferrer"&gt;TechSchool&lt;/a&gt;, an open-source platform to help people find free programming courses. The idea was simple: fight back against predatory coding bootcamps that charge thousands of dollars for content that's often worse than what's available for free on YouTube.&lt;/p&gt;

&lt;p&gt;TechSchool worked. People found great courses. The community grew. But over time, I started noticing something.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limitations of TechSchool
&lt;/h2&gt;

&lt;p&gt;TechSchool had two big problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One&lt;/strong&gt;, it was limited to tech. My friend who wanted to learn guitar couldn't use it. My mom who wanted to learn cooking couldn't use it. The core idea of community-curated courses was valuable, but I was artificially limiting who could benefit from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two&lt;/strong&gt;, adding new courses required opening a Pull Request on GitHub. Great for developers, terrible for everyone else. Most people who want to learn don't know what Git is, and they shouldn't have to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4rrcopfkefk3a2jhln0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4rrcopfkefk3a2jhln0.gif" alt="Thinking hard" width="500" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I kept thinking: what if we could build something bigger? A place where anyone could discover quality courses in &lt;strong&gt;any&lt;/strong&gt; subject, reviewed by real learners instead of marketing departments?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter CourseShelf
&lt;/h2&gt;

&lt;p&gt;CourseShelf is the spiritual successor to TechSchool. Think of it as &lt;strong&gt;Rotten Tomatoes for online learning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's the core idea: when you're looking for a movie to watch, you check Rotten Tomatoes or IMDb. You trust the crowd more than the trailer. Why should finding a good course be any different?&lt;/p&gt;

&lt;p&gt;Right now, if you want to learn something new, you either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trust the platform's algorithm (which prioritizes engagement, not quality)&lt;/li&gt;
&lt;li&gt;Trust sponsored reviews (which are basically ads)&lt;/li&gt;
&lt;li&gt;Hope you get lucky&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CourseShelf fixes this by letting real learners review and rate courses. No fake reviews. No sponsored content. Just honest opinions from people who actually completed the course.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://thecourseshelf.com" rel="noopener noreferrer"&gt;https://thecourseshelf.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31tjmfirk4k8rs5g1pa9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31tjmfirk4k8rs5g1pa9.gif" alt="Let's go" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do on CourseShelf
&lt;/h2&gt;

&lt;p&gt;Here's a quick overview of the features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discover courses&lt;/strong&gt; across any subject, not just tech&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read and write reviews&lt;/strong&gt; with star ratings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save courses to your library&lt;/strong&gt; to track what you want to learn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create playlists&lt;/strong&gt; to curate learning paths for yourself or others&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow other learners&lt;/strong&gt; to see what they're learning and recommending&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify your channel&lt;/strong&gt; if you're a course creator on YouTube&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right now, the platform supports &lt;strong&gt;YouTube courses&lt;/strong&gt; and &lt;strong&gt;books&lt;/strong&gt;. We have plans to add support for other platforms like Coursera, Skillshare, and Domestika in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  For course creators
&lt;/h2&gt;

&lt;p&gt;If you're a content creator, CourseShelf has something special for you. You can verify your YouTube channel and get a &lt;strong&gt;verified badge&lt;/strong&gt; on your profile. This helps learners identify official creators and gives you credibility.&lt;/p&gt;

&lt;p&gt;Verified creators also get access to analytics: see how many people are viewing your courses, what they're saying in reviews, and how you compare to others in your category.&lt;/p&gt;

&lt;p&gt;I created TechSchool because I was frustrated that my free courses had fewer views than paid garbage. CourseShelf is my attempt to fix the discoverability problem for all creators who are putting out quality content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tech stack
&lt;/h2&gt;

&lt;p&gt;I built CourseShelf using &lt;strong&gt;Elixir and Phoenix&lt;/strong&gt; with &lt;strong&gt;React and TypeScript&lt;/strong&gt; on the frontend via Inertia.js. If you've read my blog post about &lt;a href="https://dev.to/danielbergholz/from-nextjs-to-rails-then-elixir-my-journey-through-reactjs-burnout-h8d"&gt;leaving React for Rails and then Elixir&lt;/a&gt;, you know this is the stack I fell in love with.&lt;/p&gt;

&lt;p&gt;Phoenix gives me the productivity of Rails with the performance and reliability of the Erlang VM. Inertia.js lets me use React without the complexity of building a separate API. It's the best of both worlds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;CourseShelf is currently in &lt;strong&gt;beta&lt;/strong&gt;. We're actively adding features, fixing bugs, and improving the experience based on feedback. If you find something broken or have a suggestion, please let me know!&lt;/p&gt;

&lt;p&gt;Some features are limited during beta, but the core experience of discovering and reviewing courses is fully functional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Join the community
&lt;/h2&gt;

&lt;p&gt;I built TechSchool because I believe tech education should be accessible to everyone. I'm building CourseShelf because I believe &lt;strong&gt;all&lt;/strong&gt; education should be accessible to everyone.&lt;/p&gt;

&lt;p&gt;If you're a self-taught learner, a course creator, or just someone who's tired of wasting money on bad courses, give CourseShelf a try. And if you know a great course that deserves more attention, add it to the platform!&lt;/p&gt;

&lt;p&gt;Let's build the largest community-curated database of online courses together. We are all gonna make it 🔥&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqek6gbi1lugsas1ryft.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqek6gbi1lugsas1ryft.gif" alt="We're all gonna make it" width="500" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow us
&lt;/h2&gt;

&lt;p&gt;Stay up to date with new features and announcements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter: &lt;a href="https://x.com/courseshelf" rel="noopener noreferrer"&gt;@courseshelf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LinkedIn: &lt;a href="https://www.linkedin.com/company/courseshelf" rel="noopener noreferrer"&gt;CourseShelf&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thank you
&lt;/h2&gt;

&lt;p&gt;If you reached the end of this post, thank you for your time. Building in public is scary, but seeing people use something I created makes it worth it.&lt;/p&gt;

&lt;p&gt;Have questions? Want to chat? Find me on Twitter &lt;a href="https://twitter.com/danielbergholz" rel="noopener noreferrer"&gt;@danielbergholz&lt;/a&gt; or drop me an email.&lt;/p&gt;

&lt;p&gt;See you on CourseShelf!&lt;/p&gt;

</description>
      <category>learning</category>
      <category>beginners</category>
      <category>elixir</category>
      <category>react</category>
    </item>
    <item>
      <title>My AI-Powered Workflow for Writing Elixir and Phoenix with Windsurf</title>
      <dc:creator>Daniel Bergholz</dc:creator>
      <pubDate>Mon, 10 Mar 2025 14:41:03 +0000</pubDate>
      <link>https://dev.to/danielbergholz/my-ai-powered-workflow-for-writing-elixir-and-phoenix-with-windsurf-4k8m</link>
      <guid>https://dev.to/danielbergholz/my-ai-powered-workflow-for-writing-elixir-and-phoenix-with-windsurf-4k8m</guid>
      <description>&lt;p&gt;There's a ton of stuff out there about using AI to write JavaScript and Next.js code, but what about Elixir and Phoenix? Are they getting left behind in the LLM code generation game? Well, yes and no. Can we make it better? Definitely. Is it worth the effort? Hell yeah. And can we actually become those legendary 10x engineers by writing Elixir with AI-powered tools like &lt;a href="https://www.cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; or &lt;a href="https://codeium.com/windsurf" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt;? You bet we can!&lt;/p&gt;

&lt;h2&gt;
  
  
  First, why Windsurf and not Cursor?
&lt;/h2&gt;

&lt;p&gt;This year I tried using both Cursor and Windsurf for a month to see which one I'd stick with. The code they generated was pretty much the same (not surprising since they both use &lt;a href="https://claude.ai/" rel="noopener noreferrer"&gt;Claude&lt;/a&gt; 3.5 Sonnet). But Windsurf was better at understanding my codebase and grabbing ideas from my existing files, plus its UI just felt smoother and more intuitive. Honestly though, these AI tools are copying each other's features so quickly that it probably doesn't matter which one you pick - they're both solid options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsfy7k34opmrsmq61n7y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsfy7k34opmrsmq61n7y.png" alt=" " width="602" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Does AI generate good Elixir and Phoenix code?
&lt;/h2&gt;

&lt;p&gt;AI can handle the basics pretty well. Need a simple controller that renders a page? Or a basic &lt;a href="https://hexdocs.pm/phoenix_live_view/welcome.html" rel="noopener noreferrer"&gt;LiveView&lt;/a&gt; with a bit of server-side state? Claude's got you covered.&lt;/p&gt;

&lt;p&gt;But here's the thing - the more niche your tech stack gets, the less reliable AI becomes. Take my setup for example: I use Elixir, Phoenix, React, and &lt;a href="https://inertiajs.com" rel="noopener noreferrer"&gt;Inertia&lt;/a&gt; for my side projects. The &lt;a href="https://hexdocs.pm/inertia/readme.html" rel="noopener noreferrer"&gt;Inertia adapter for Phoenix&lt;/a&gt; is super new and not widely adopted yet (which is a shame - this combo is incredibly productive and performant). There's just no way current AI models have enough training data on this specific stack to generate useful code. The more specialized your tools, the more you'll need to rely on your own expertise rather than AI assistance.&lt;/p&gt;

&lt;p&gt;This isn't just an Elixir thing - it's the same story with any tech that hasn't gone mainstream yet. Languages like Zig, Gleam, or Scala (and whatever frameworks people build with them) just don't have nearly as much code floating around online as JavaScript or Python do.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to improve?
&lt;/h2&gt;

&lt;p&gt;I mainly use two features in Windsurf: &lt;a href="https://docs.codeium.com/windsurf/memories" rel="noopener noreferrer"&gt;memories and rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memories&lt;/strong&gt; are like Windsurf's personalized notepad. Did Claude mess up by using &lt;code&gt;assign/3&lt;/code&gt; instead of &lt;code&gt;assign_prop/3&lt;/code&gt; inside a controller to pass Elixir data to React? Just point out the mistake. If Windsurf thinks it's important, it'll remember this for your entire coding session and avoid repeating the error.&lt;/p&gt;

&lt;p&gt;You can be direct about it too: "Hey, you got X wrong. The correct way is Y. Please create a memory for this."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rules&lt;/strong&gt; are more permanent and explicit. Just create a &lt;code&gt;.windsurfrules&lt;/code&gt; file in your project's root directory with specific instructions (similar to how &lt;code&gt;.cursorrules&lt;/code&gt; works). These rules will guide Windsurf every time it helps with your code.&lt;/p&gt;

&lt;p&gt;Here is my current &lt;code&gt;.windsurfrules&lt;/code&gt; for Phoenix + Inertia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;You are an expert in Elixir, Phoenix, PostgreSQL, JavaScript, TypeScript, React, Inertia, and Tailwind CSS.

&lt;span class="gh"&gt;# Elixir and Phoenix Usage&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; In controllers, use &lt;span class="sb"&gt;`assign_prop/3`&lt;/span&gt; to assign props to the Inertia page and then &lt;span class="sb"&gt;`render_inertia/2`&lt;/span&gt; to render Inertia pages.
&lt;span class="p"&gt;-&lt;/span&gt; In controllers tests, use &lt;span class="sb"&gt;`inertia_component/1`&lt;/span&gt; to assert the component name and &lt;span class="sb"&gt;`inertia_props/1`&lt;/span&gt; to assert the props.
&lt;span class="p"&gt;-&lt;/span&gt; When generating migrations, use &lt;span class="sb"&gt;`mix ecto.gen.migration &amp;lt;name&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use plural form for context modules (e.g., "Users" for users table)
&lt;span class="p"&gt;-&lt;/span&gt; Use singular form for schema modules (e.g., "User" for users table)
&lt;span class="p"&gt;-&lt;/span&gt; Context files are usually inside a folder named after the resource (e.g., lib/my_app/users.ex)
&lt;span class="p"&gt;-&lt;/span&gt; Schema files are usually inside a folder named after the resource (e.g., lib/my_app/users/user.ex)
&lt;span class="p"&gt;-&lt;/span&gt; Prefer keyword-based queries over pipe-based queries
&lt;span class="p"&gt;  -&lt;/span&gt; For example, use &lt;span class="sb"&gt;`from(u in User, where: u.age &amp;gt; 18, select: u)`&lt;/span&gt; over &lt;span class="sb"&gt;`User |&amp;gt; where(age: 18) |&amp;gt; select([u], u)`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`dbg/1`&lt;/span&gt; to debug code.

&lt;span class="gh"&gt;# React and Inertia Usage&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Pages are in assets/js/pages. Use default export for pages.
&lt;span class="p"&gt;-&lt;/span&gt; Components are in assets/js/components. Use named exports for components.
&lt;span class="p"&gt;-&lt;/span&gt; Utils are in assets/js/lib.
&lt;span class="p"&gt;-&lt;/span&gt; Inside pages, get the props from the controller as regular props from the function argument.
&lt;span class="p"&gt;-&lt;/span&gt; When dealing with forms, use the &lt;span class="sb"&gt;`useForm`&lt;/span&gt; hook from Inertia
&lt;span class="p"&gt;-&lt;/span&gt; Use absolute paths for local imports using &lt;span class="sb"&gt;`@/`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; If you need to merge tailwind classes, use the &lt;span class="sb"&gt;`cn`&lt;/span&gt; function from assets/js/lib/utils.ts.
&lt;span class="p"&gt;-&lt;/span&gt; Import Radix components from "radix-ui", not from "@radix-ui/react-dialog" or "@radix-ui/react-button" etc.
&lt;span class="p"&gt;-&lt;/span&gt; Always create the mobile version of the component along with the desktop version.
&lt;span class="p"&gt;-&lt;/span&gt; Use lucide-react for icons.
&lt;span class="p"&gt;-&lt;/span&gt; Use kebab-case for file names.
&lt;span class="p"&gt;-&lt;/span&gt; If the page or component uses a type for a resource from the database, like users or courses, create the type in the assets/js/types folder.
&lt;span class="p"&gt;-&lt;/span&gt; Prefer types over interfaces.

&lt;span class="gh"&gt;# General Usage&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; When debugging data from the database, if the postgres_my_app MCP is not avaiable, use &lt;span class="sb"&gt;`psql my_app_dev -c "&amp;lt;your query&amp;gt;"`&lt;/span&gt; to connect to the database and then run the query. There is also the my_app_test database for testing.
&lt;span class="p"&gt;-&lt;/span&gt; Use the &lt;span class="sb"&gt;`mix check`&lt;/span&gt; command after generating lots of files to check the Elixir and React code for errors and code quality. If you encounter format errors, use &lt;span class="sb"&gt;`mix format`&lt;/span&gt; to fix them.
&lt;span class="p"&gt;-&lt;/span&gt; If any of my requests are not clear, ask me to clarify.
&lt;span class="p"&gt;-&lt;/span&gt; If you have better suggestions, feel free to suggest them.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting Up-to-Date Info
&lt;/h2&gt;

&lt;p&gt;This is a super underrated feature when working with frameworks that change all the time (like Next.js) or more niche ones (like Phoenix). Remember: AI is great at general knowledge, but the more specific you go, the less likely it'll get things right. To fix this, just use the &lt;code&gt;@web&lt;/code&gt; directive and drop in links to any documentation you think is relevant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftusowab3ce6y9bi8rcac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftusowab3ce6y9bi8rcac.png" alt=" " width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Check After a Long Coding Session
&lt;/h2&gt;

&lt;p&gt;It's all fun and games when Claude spits out 20 new files and everything looks fine. But are you actually checking all these files for formatting issues, type errors, or compilation warnings? And how do you make sure your code quality isn't tanking over time? Sometimes AI misunderstands what you want and goes off on a tangent, potentially wrecking hours of your hard work.&lt;/p&gt;

&lt;p&gt;That's why I made a simple script called "check" (my mental "sanity check") that I ask Claude to run after long coding sessions to make sure everything's still good.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mix.exs&lt;/span&gt;

&lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;aliases&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="ss"&gt;check:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"format --check-formatted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"cmd npm run typecheck --prefix assets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"deps.unlock --check-unused"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"compile --warnings-as-errors"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"credo --strict"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script does a few key things: checks for formatting issues (my &lt;code&gt;.windsurfrules&lt;/code&gt; file tells Claude to just run &lt;code&gt;mix format&lt;/code&gt; to fix these), runs the "typecheck" script from my NPM project (React + Inertia) in the assets folder, looks for unused dependencies, checks for compilation warnings like unused variables or aliases, and finally runs &lt;a href="https://hexdocs.pm/credo/overview.html" rel="noopener noreferrer"&gt;credo&lt;/a&gt; in strict mode to catch problems like oversized functions or deeply nested conditionals.&lt;/p&gt;

&lt;p&gt;This way, you can sleep easy knowing your app not only works but also has solid code quality with no warnings or errors hanging around. This is just my version of the check command - feel free to create your own with checks that make sense for your project. And if you want Claude to run both the "check" script and your tests after generating code, just add that rule to your &lt;code&gt;.windsurfrules&lt;/code&gt; file!&lt;/p&gt;

&lt;h2&gt;
  
  
  A Word of Caution
&lt;/h2&gt;

&lt;p&gt;This pro tip isn't specific to Elixir, but worth mentioning: be careful with generating code using AI - it can get expensive fast. Don't let the $15/month price tag fool you. This month alone I bought extra credits for Windsurf 3 times ($10 per 300 new credits). That brought my monthly cost to $45!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvigomphy81iqyffvsw56.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvigomphy81iqyffvsw56.gif" alt="burning money" width="500" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How to avoid this? My rule is simple: only ask Claude to generate code once I'm really confident about what I want to build. If you have a clear PRD (Product Requirements Document), there's a 95% chance Claude will "one shot" the solution. This is way better than going back and forth, generating code, rejecting it, and generating again. &lt;/p&gt;

&lt;p&gt;TL;DR: Spend more time planning rather than generating code. In this new era, code has become a "low level thing" that machines generate, while we humans can focus on higher level problems like crafting a good plan for an MVP, figuring out what users actually want, achieving product-market fit for our startup, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moral of the story
&lt;/h2&gt;

&lt;p&gt;Look, AI definitely has some blind spots with Elixir and Phoenix code - but as I've shown throughout this post, there are some pretty simple workarounds for most of these issues.&lt;/p&gt;

&lt;p&gt;From my experience, the productivity boost is totally worth dealing with these little hiccups. That mythical "10x engineer" everyone talks about? It's not just hype - anyone can get there if they're willing to embrace these new tools.&lt;/p&gt;

&lt;p&gt;The real trick is keeping an open mind and constantly trying new stuff. Don't get stuck doing things the old way just because that's how you've always done it.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>windsurf</category>
      <category>ai</category>
      <category>react</category>
    </item>
    <item>
      <title>Announcing TechSchool: A free and open-source platform to learn programming</title>
      <dc:creator>Daniel Bergholz</dc:creator>
      <pubDate>Tue, 05 Mar 2024 22:52:54 +0000</pubDate>
      <link>https://dev.to/danielbergholz/announcing-techschool-a-free-and-open-source-platform-to-learn-programming-47fk</link>
      <guid>https://dev.to/danielbergholz/announcing-techschool-a-free-and-open-source-platform-to-learn-programming-47fk</guid>
      <description>&lt;p&gt;Since 2019 I have published free courses on my &lt;a href="https://youtube.com/@DanielBergholz?si=WsZ062ZtA5MV3kX_" rel="noopener noreferrer"&gt;YouTube channel&lt;/a&gt;. Many times, people have commented on my videos something like "Wow, this course is amazing! It's a lot better than the expensive course I purchased!". I started reflecting after that. Why on earth is someone getting paid thousands of dollars selling a course that is worse than the one I made for &lt;strong&gt;free&lt;/strong&gt;? Also, why does my course only have 100 views on YouTube? This isn't fair.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the problem
&lt;/h2&gt;

&lt;p&gt;There are lots of people who want to learn programming and also lots of free courses available. However, most of the time, these two groups don't meet each other.&lt;/p&gt;

&lt;p&gt;Why? Two reasons. One is that multiple VC-backed predatory coding bootcamps spend millions of dollars targeting newcomers and telling them that the only way to get into the industry is by spending all their money on expensive online courses. Two, the YouTube algorithm is super hard to master. If you recently created a channel, it can take &lt;strong&gt;years&lt;/strong&gt; to reach a bigger audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;What if we could easily find all the free courses out there without the YouTube algorithm standing in our way? That's where TechSchool steps in. It is a platform that contains all the free content that might be flying under the radar. It is also open-source, which means anyone who knows a cool course can easily open a PR to add it.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://techschool.dev" rel="noopener noreferrer"&gt;https://techschool.dev&lt;/a&gt;&lt;br&gt;
Discord: &lt;a href="https://discord.gg/C4abRX5skH" rel="noopener noreferrer"&gt;https://discord.gg/C4abRX5skH&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/danielbergholz/techschool.dev" rel="noopener noreferrer"&gt;https://github.com/danielbergholz/techschool.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's fight back against the expensive barrier to entry! Tech education should be free and accessible to everyone! We are all gonna make it 🔥&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbrk54y1yfc2srctfygp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbrk54y1yfc2srctfygp.gif" alt="Let's fight back!" width="480" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;TechSchool is forever a work in progress. We are all collectively adding more content over time, so if you don't like the options available right now, wait a couple of weeks and then re-visit the website! I'm sure you'll find something new.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tech stack
&lt;/h2&gt;

&lt;p&gt;I have it all explained on the &lt;a href="https://github.com/danielbergholz/techschool.dev/blob/main/docs/tech-stack.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;, but in summary, I decided to use Elixir and Phoenix, because it's a freaking AWESOME combo. I'm also using Live View on all pages, so hopefully the transition between them is super smooth!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>javascript</category>
      <category>ruby</category>
      <category>python</category>
    </item>
    <item>
      <title>From Next.js to Rails then Elixir: My journey through React.js burnout</title>
      <dc:creator>Daniel Bergholz</dc:creator>
      <pubDate>Wed, 10 Jan 2024 23:29:41 +0000</pubDate>
      <link>https://dev.to/danielbergholz/from-nextjs-to-rails-then-elixir-my-journey-through-reactjs-burnout-h8d</link>
      <guid>https://dev.to/danielbergholz/from-nextjs-to-rails-then-elixir-my-journey-through-reactjs-burnout-h8d</guid>
      <description>&lt;p&gt;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".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qu5amub6gpg909tljmz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qu5amub6gpg909tljmz.gif" alt="This is the way" width="480" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result, I got used to a &lt;strong&gt;huge&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;Until one day I said &lt;strong&gt;ENOUGH IS ENOUGH!&lt;/strong&gt; 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!&lt;/p&gt;




&lt;h2&gt;
  
  
  The timeline of the burnout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.gatsbyjs.com/" rel="noopener noreferrer"&gt;Gatsby.js&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;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 &lt;code&gt;create-react-app&lt;/code&gt; 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?&lt;/p&gt;

&lt;p&gt;I decided to learn this new framework by reading the docs and let me tell you, this was &lt;strong&gt;NOT&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8accuzmxat7v76cpb8u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8accuzmxat7v76cpb8u.gif" alt="Try harder" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;LOT&lt;/strong&gt; of my developer friends started saying "Gatsby is dead! Next was created to simplify static site generation and also provide server-side rendering".&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I took a quick glance over the Next documentation and &lt;strong&gt;immediately&lt;/strong&gt; 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.&lt;/p&gt;

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

&lt;p&gt;Unfortunately, something I learned the hard way: In the JavaScript ecosystem, all the good things come to an end.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://remix.run/" rel="noopener noreferrer"&gt;Remix&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;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 &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;JAMstack&lt;/a&gt; are thrown away here? No way, this framework ain't gonna last". However, to my surprise, not only did Remix survive, but it was &lt;a href="https://shopify.engineering/remix-joins-shopify" rel="noopener noreferrer"&gt;acquired by Shopify&lt;/a&gt; and emerged as a prominent competitor to Next.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;loader&lt;/code&gt; function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leaving the Matrix
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuewu9wfathbfo889mwya.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuewu9wfathbfo889mwya.gif" alt="Leaving the Matrix" width="450" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  I'm DONE with Next and Vercel
&lt;/h3&gt;

&lt;p&gt;I reached my tipping point with &lt;a href="https://nextjs.org/docs/app" rel="noopener noreferrer"&gt;Next.js app router&lt;/a&gt;. Here is a comprehensive list of everything wrong that Vercel is pushing to Next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What was once simple: The &lt;code&gt;getStaticProps&lt;/code&gt; and &lt;code&gt;getServerSideProps&lt;/code&gt; 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.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp41ds14loo21xgimcza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp41ds14loo21xgimcza.png" alt="Horrible Next.js code" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ye40ykjgrd3z10t5nx7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ye40ykjgrd3z10t5nx7.png" alt="Vercel is corrupting React" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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. &lt;/p&gt;

&lt;p&gt;Not surprisingly, other content creators were going through a similar situation:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/watch?si=z3-FDVgcB3xfp06h&amp;amp;v=zkCBSz353fc&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;



&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/watch?si=10fy1d-ZoB7t3Uc_&amp;amp;v=Zt8mO_Aqzw8&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  The Path to Enlightenment
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batteries included&lt;/li&gt;
&lt;li&gt;Convention over configuration&lt;/li&gt;
&lt;li&gt;Good developer experience&lt;/li&gt;
&lt;li&gt;Modern and performant frontend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My first instinct was to take a look at the top frameworks from the &lt;a href="https://survey.stackoverflow.co/2023/#section-most-popular-technologies-web-frameworks-and-technologies" rel="noopener noreferrer"&gt;Stack Overflow Survey 2023&lt;/a&gt;. 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).&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt; and &lt;a href="https://alpinejs.dev/" rel="noopener noreferrer"&gt;Alpine&lt;/a&gt;, however, both are external dependencies that you need to install.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://inertiajs.com/" rel="noopener noreferrer"&gt;inertia.js&lt;/a&gt;, 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 &lt;code&gt;$&lt;/code&gt; and &lt;code&gt;-&amp;gt;&lt;/code&gt; everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruby on Rails
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;by far&lt;/strong&gt; the easiest to read and the most beautiful language I've ever seen. I quickly realized that &lt;a href="https://world.hey.com/dhh/a-writer-s-ruby-2050b634" rel="noopener noreferrer"&gt;writing visually pleasing code&lt;/a&gt; was a priority for the Rails team, and that was new to me.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;On the frontend side, there is &lt;a href="https://hotwired.dev/" rel="noopener noreferrer"&gt;Hotwire&lt;/a&gt;, 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.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;rails scaffold&lt;/code&gt; command. And immediatly I was &lt;strong&gt;SHOCKED&lt;/strong&gt;. One single command generates everything I need for a CRUD? No way!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58lbioexmot9412kojr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58lbioexmot9412kojr5.png" alt="Image description" width="800" height="914"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;After the initial shock from &lt;code&gt;rails scaffold&lt;/code&gt;, I was shocked once again when I opened the code from the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticlesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;edit&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;

    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;root_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :see_other&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;article_params&lt;/span&gt;
      &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:article&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;And to finish my frustrations, I started writing frontend code. How do I create components? &lt;a href="https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials" rel="noopener noreferrer"&gt;Partials&lt;/a&gt;. 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 &lt;a href="https://stimulus.hotwired.dev/" rel="noopener noreferrer"&gt;Stimulus&lt;/a&gt;, 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.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/controllers/slideshow_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;targets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slide&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showCurrentSlide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showCurrentSlide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showCurrentSlide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;showCurrentSlide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slideTargets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;
  
  
  Elixir and Phoenix
&lt;/h3&gt;

&lt;p&gt;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:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/live/bfrzGXM-Z88?si=Xsa7yCKeVSY5R3sT" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://hexdocs.pm/" rel="noopener noreferrer"&gt;Hex Docs&lt;/a&gt;, you just need to get used to one interface in order to learn new things.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// JS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;daniel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

&lt;span class="c1"&gt;// result: obj = {name: "daniel", age: 25}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Elixir&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"daniel"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# result: obj = %{name: "daniel", age: 25}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or you could achieve the same thing with a simpler syntax using the pipe operator:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Elixir with pipe operator&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"daniel"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# result: obj = %{name: "daniel", age: 25}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;How about the main framework? Is Phoenix batteries included? Convention over configuration? &lt;strong&gt;YES IT IS!&lt;/strong&gt; 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.&lt;/p&gt;

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

&lt;p&gt;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 &lt;a href="https://marketplace.visualstudio.com/items?itemName=JakeBecker.elixir-ls" rel="noopener noreferrer"&gt;ElixirLS extension&lt;/a&gt; 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!&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://elixir-lang.org/blog/2022/10/05/my-future-with-elixir-set-theoretic-types/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Felixir-lang.org%2Fimages%2Fsocial%2Felixir-og-card.jpg" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://elixir-lang.org/blog/2022/10/05/my-future-with-elixir-set-theoretic-types/" rel="noopener noreferrer" class="c-link"&gt;
          My Future with Elixir: set-theoretic types - The Elixir programming language
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          We announce and explore the possibilities for bringing set-theoretic types into Elixir.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Felixir-lang.org%2Ffavicon.ico" width="800" height="400"&gt;
        elixir-lang.org
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;My final concern on the frontend was addressed by Phoenix &lt;a href="https://hexdocs.pm/phoenix_live_view/welcome.html" rel="noopener noreferrer"&gt;Live View&lt;/a&gt;. On the code side, this was the exact piece of the documentation's home page that convinced me:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5sjzj90khytebnk523fm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5sjzj90khytebnk523fm.png" alt="Image description" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;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:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/watch?si=ZoWAlPjQC-svmV3Y&amp;amp;v=wrmVk2czqMg&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;


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


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/watch?si=gAow6oIjgf8_OTkg&amp;amp;v=MZvmYaFkNJI&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen4ls4vpepbcofv2dd3e.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen4ls4vpepbcofv2dd3e.gif" alt="The end" width="499" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>rails</category>
      <category>elixir</category>
      <category>react</category>
    </item>
  </channel>
</rss>
