zhenfeng-zhu

zhenfeng-zhu

Nex - 0.3.x Design Philosophy: The Evolution of Minimalism

Nex - A minimalist web framework for indie hackers and startups

A Note Before We Begin

This document records my thoughts during the development of Nex.

Special thanks to the friends from Reddit (r/elixir) and Elixir Forum. Your feedback, questions, suggestions, and even criticism have helped me gradually understand what Nex should be. Without you, there would be no 0.3.x refactor.

I also want to thank the teams behind Next.js, Phoenix, and HTMX. Your work has been an endless source of inspiration.


TL;DR

Simply put, 0.2.x was too complex: 4 different use statements, confusing API parameters, and unconventional directory naming.

With 0.3.x, I decided to subtract:

  • Only one use Nex.
  • API parameters reduced to just query and body, like Next.js.
  • Directories renamed back to the familiar components/.
  • Streaming responses became a simple function Nex.stream/1.

The result: less code, fewer concepts, and a smoother development experience.


The Core Problem: Finding Our Identity

While developing Nex 0.2.x, I was genuinely conflicted. I kept asking myself: What should Nex actually be? I tried many approaches, referenced many frameworks, but always felt something was missing.

The Dilemma of 0.2.x

Look at this 0.2.x code:

defmodule MyApp.Pages.Index do
  use Nex.Page  # Explicitly declare this is a Page
end

defmodule MyApp.Api.Users do
  use Nex.Api  # Explicitly declare this is an Api
end

defmodule MyApp.Partials.Card do
  use Nex.Partial  # Explicitly declare this is a Partial
end

This design looked rigorous, but it was exhausting to write. Since files are already in the pages/ directory, why should I have to tell the framework again, “This is a Page”?

Learning from Next.js

Later, I revisited Next.js. Its greatest strength is convention over configuration. You don’t need to write any configuration code—just put files in the right place.

This is what Nex should be: let developers focus on business logic and write less boilerplate.


Decision 1: One use Nex for Everything

The Awkwardness Before

In 0.2.x, not only did you have to remember 4 different modules, you also had to understand the differences between them. This was entirely artificial cognitive burden.

The Approach Now

0.3.x uses Elixir’s macro system to automatically infer module types based on file paths.

# 0.3.x - Much cleaner
defmodule MyApp.Pages.Index do
  use Nex
end

defmodule MyApp.Api.Users do
  use Nex
end

While this is a Breaking Change, it makes the code look much cleaner.


Decision 2: Rename partials/ to components/

This was actually a long-overdue correction.

I initially used partials because I was influenced by Rails and thought it had more of a “server-side rendering” flavor. But the reality is, the modern frontend world (React, Vue, Svelte) and Phoenix 1.7+ all use components.

Forcing partials only confused new users and offered no benefits. So we embraced the change and switched back to the familiar components.


Decision 3: Writing REST APIs Is Finally Easy

The Pain Point

Writing APIs in 0.2.x was torture. I had 4 places to put parameters: params, path_params, query_params, body_params. Developers had to think about where each parameter came from, adding cognitive burden.

Every time you wrote code, you had to wonder:

  • “Is this id in the path or in the query?”
  • “Should I use params or query_params?”
  • “What’s the difference between body_params and params?”

Learning from Next.js

I looked at how Next.js does it. They offer only two options, yet cover all scenarios:

  1. req.query: Handles all GET request parameters (whether in the path or after the ?)
  2. req.body: Handles all POST/PUT data

This is brilliantly simple. Developers only care about “Do I need to fetch data from the URL (Query)” or “Do I need to fetch data from the body (Body)”.

So in 0.3.x, we do the same. The framework automatically unifies path parameters (like /users/:id) and query parameters (like ?page=1) into req.query:

def get(req) do
  # Both path parameter :id and query parameter ?page are here
  id = req.query["id"]
  page = req.query["page"]
end

def post(req) do
  # All submitted data is here
  user = req.body["user"]
end

This “no-brainer” experience is what a good framework should provide.


Decision 4: Streaming Responses Are First-Class Citizens

In 2025, if a web framework requires effort to support SSE (Server-Sent Events), it’s definitely outdated.

In 0.2.x, you needed use Nex.SSE and had to follow specific function signatures. But in the age of AI applications, streaming responses should be a standard capability available everywhere.

Now you can return a stream from anywhere:

def get(req) do
  Nex.stream(fn send ->
    send.("Hello")
    Process.sleep(1000)
    send.("World")
  end)
end

Simple and direct, no magic tricks.


Summary: Finding Our Identity

When developing 0.1.x and 0.2.x, I was a bit greedy. I wanted to combine Phoenix’s power, Next.js’s simplicity, and Rails’s classics all together. The result was a “Frankenstein” framework.

By 0.3.x, I finally figured it out: Nex should not try to be another Phoenix.

The Elixir community already has Phoenix, a perfect industrial-grade framework. Nex’s mission should be to provide a simple and lightweight alternative (core code < 500 lines). It should be like Next.js, enabling developers (especially indie developers) to rapidly build usable products.

This is the entire point of Nex 0.3.x: Embrace simplicity, return to developer intuition.


Future Outlook

This refactor is not just about API changes, but a shift in design philosophy

Next Steps

  1. Exploring Datastar Integration

    • Monitor Datastar’s development as a Hypermedia framework
    • Evaluate whether it can provide finer-grained state updates than HTMX
    • Stay open to emerging technologies, but prioritize core DX improvements
  2. Ultimate Developer Experience (DX)

    • Make the framework “better to use”, not “more features”
    • More comprehensive documentation and real-world examples

Core Values Remain Unchanged

No matter how Nex evolves, these core principles won’t change:

  • :white_check_mark: Minimal: Least code, maximum productivity
  • :white_check_mark: Modern: Aligned with modern framework best practices
  • :white_check_mark: Practical: Solving real-world problems

A Word to Developers

About Breaking Changes

Nex is currently in an early, fast-moving iteration phase. To pursue the ultimate developer experience, breaking changes may happen at any time.

But I promise: I will document the thinking and reasoning behind every refactor in detail.

It’s not about change for change’s sake, but about exploring the best development experience for Elixir. I hope that by sharing these thoughts, we can communicate, learn together, and collectively refine a truly great framework.

Rather than giving a cold “upgrade guide”, I prefer to tell you “why I’m doing this”.

My Promise to Users

  1. Minimal API: Only need to learn use Nex and a few response functions
  2. Familiar Developer Experience: If you know Next.js, Nex’s API design will feel natural
  3. Comprehensive Documentation: Complete tutorials from beginner to advanced
  4. Active Community: We will continue to improve and support

Nex 0.3.x - Minimal, Modern, Practical Elixir Web Framework

Let’s build better web applications together! :rocket:

Nex Github Repo: GitHub - gofenix/nex: Nex – A minimalist web framework for indie hackers and startups · GitHub

Quick Start:

# Install the project generator
mix archive.install hex nex_new

# Create a new project
mix nex.new my_app
cd my_app

# Start development server
mix nex.dev

Most Liked

derek-zhou

derek-zhou

I love minimalism, but minimalism never sells. As a indie hacker, why would I adopt a simple and lightweight framework (core code < 500 lines) when I can write the 500 lines myself and have it exactly my way? I would suggest you find a niche and optimize for it.

Minimalism in code size is nice, but only to the author himself. Minimalism on the API surface area will be more valuable to the users.

derek-zhou

derek-zhou

I actually think it is a great mentality to design a framework. Your framework has to serve someone, and that someone would have to be yourself in the beginning. (Dogfooding). My point was: evangelizing should come after you have dogfood’ed yourself for a few diverse projects, so you can prove that you didn’t design yourself into a corner, or you have rescued yourself from the original corner. At that stage, you most likely cannot claim the doctrine of minimalism anymore.

tfwright

tfwright

Maybe I didn’t sufficiently highlight the definition I think is broadly accepted:

I’m not sure the claim that it’s broadly accepted is really that debatable tbh, but is there a reason it doesn’t seem clear/useful?

Last Post!

c4lliope

c4lliope

@GrammAcc and @dimitarvp - I’m finally catching up here. You really dug into package management!

I’m thrilled to read (only some) of this (at some point, send a DM!), but once again - let’s keep this forum for Elixir! For cross-language concerns, I’d recommend the Linux Foundation CHAOSS group (community health analytics in open-source-software). You can hop on the slack here, and there’s a #wg-package-metadata channel for discussion of package managers, authorship, and dependency-responsibility.

I do hope we keep in mind all the labor that @zhenfeng-zhu has gone through to bring us a new approach, because discussions of camel or snake case are surely more practical than philosophical.

For a more on-theme practical discussion, I am opening a thread to explore how to bridge my current Phoenix app with new pages managed by Nex. [Chime in here!](Gradually upgrading, phoenix → nex using pluggable routes)

Where Next?

Popular in News & Updates Top

bartblast
Hologram v0.8.0 is out! This release brings JavaScript interoperability - the most requested feature since the project’s inception. You c...
New
jjcarstens
Do you like Hacktoberfest? Also enjoy working with Nerves and want to contribute? Fantastic! :tada: :beers: Here are some potential sta...
New
bartblast
Hologram v0.10 is out! The headline is the event system, which got a lot bigger this release. You can now handle keyboard, scroll, resize...
New
mobileoverlord
New versions of the Kiosk systems are out. These systems update official Nerves systems with a local web browser for rendering user inter...
New
hugobarauna
Discover Livebook 0.9’s new security features, including Hubs for centralized secret management, notebook stamping, and a sneak peek into...
New
DominikWolek
Hi everyone! After about 3 months of really hard work, we have released the very first version of Jellyfish Media Server! The best star...
New
hugobarauna
Thanks to advancements in the overall Numerical Elixir ecosystem, Livebook v0.11 includes a highly improved integration with Whisper. To...
New
mobileoverlord
Action Advised: NervesHub Sunset In 2018, NervesHub was launched to deliver first-class support for hardware deployments directly from th...
New
zachdaniel
Hey folks, made some recent performance improvements to spark, the tool underlying all of our DSLs. GitHub - ash-project/spark: Tooling f...
New
fhunleth
The Nerves core team is proud to announce the Nerves v1.8.0 release. Nerves provides the core tooling for creating self-contained, BEAM-...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 42158 114
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39523 209
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31307 143
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New

We're in Beta

About us Mission Statement