Summary: Solid 2 versus Octane

solid-2-versus-octane-summary.md

Solid 2 vs Octane: Two Different Ideas of What a Framework Should Know

Last updated in response to this comment.

The shortest version: Solid 2 makes reactive relationships part of the running program. Octane tries to keep application code closer to ordinary JavaScript while giving a compiler more responsibility for understanding and optimizing it. The debate comes down to what reactive code should mean when you write it, and what information should remain available when the app is running.

The thread began as a developer-experience scorecard. After several rounds between Dominic Gannaway, creator of Octane, and Ryan Carniato, creator of Solid, the disagreement has become both narrower and more interesting.

It is not really compiler vs runtime. Both use both.

It is about two questions.

1. What should the code itself mean?

Solid: reading reactive state establishes a relationship. The system remembers that one thing depends on another.

Octane: ordinary calculations should stay ordinary wherever possible. Inside a component, const total = price * quantity simply calculates a value when the component runs. The compiler can remove unnecessary work later.1

This sounds like a small distinction, but it explains much of the debate.

In Solid, where you read something matters because reads build the dependency graph. Move a read outside its tracking context, or pass a value instead of the reactive getter, and you may change the relationship.1

Ryan considers that relationship the point. Dominic considers it a useful capability with a semantic cost: code that looks like an ordinary value read can participate in behavior that depends on tracking context.

That is Octane's strongest argument for its authoring model. It wants developers to begin closer to normal JavaScript and make the compiler responsible for more of the machinery.

2. What should the system still know at runtime?

This is Ryan's strongest argument for Solid.

Solid's dependency graph actually exists while the program runs. The framework can inspect what depends on what, what is pending, and what caused reactive work because those relationships were recorded from the execution that actually happened.2

Ryan argues that this is fundamentally useful for debugging. A compiler can infer a great deal before execution, but the live program knows which branch was taken, which value was read, and which dependency exists right now.

Dominic now largely agrees with the premise but not the conclusion.

He accepts that Octane currently does not expose the same continuous view of causality as Solid. His bet is that it can combine compiler knowledge with source-linked runtime information—render causes, state, async status, instrumentation, and future devtools—without making a single fine-grained dependency graph the model for everything.3

So the observability question is now genuinely open:

Does excellent debugging require preserving the actual dependency graph, or can a compiler-oriented framework retain enough runtime evidence to explain what happened without one universal graph?

Ryan bets on the former. Dominic bets on the latter.

The async disagreement has narrowed considerably

Earlier in the thread, async looked like a much larger philosophical divide.

Ryan clarified that Solid 2 deliberately uses a “colorless” async model. A reactive source can change from immediately available to temporarily pending without forcing all downstream code to become visibly async or start carrying Promise types. Readiness travels through the reactive system and is dealt with where somebody actually needs to care about it.4

Dominic now explicitly agrees that this is valuable. He is not arguing for viral async/Promise coloring.5

His remaining concern is the more general one above: a tracked read preserves a relationship that an ordinary value read does not.

That makes the real disagreement cleaner:

  • Ryan: whether a reactive value is ready is a dynamic fact the runtime can answer.
  • Dominic: fine—but tracked reads still give source expressions semantics that depend on execution context, and Octane is deliberately trying to preserve a simpler default contract.

In other words, the two creators now mostly agree on the desired sync-to-async ergonomics. They differ more on how much reactivity should be embedded in the meaning of a read.

A stricter Octane changes the comparison—but does not erase familiarity

Octane's "use strong" direction lets the compiler reject patterns that it cannot support safely rather than merely optimizing whatever JavaScript it receives. Dominic wants every restriction to buy a concrete guarantee.6

Ryan points out that this weakens one of Octane's original selling points. If Octane eventually forbids familiar React habits, then Octane also asks developers to learn a new model.

Dominic's answer is persuasive: familiarity is not all-or-nothing.

A stricter Octane can still preserve familiar concepts such as components, props, state, composition, and much existing application structure while changing particular rules around effects, memoization, or coordination.7

The comparison is therefore not:

Solid = new model
Octane = React you already know

It is closer to:

Solid: learn explicit reactive semantics.
Octane: retain more familiar source semantics, but increasingly accept compiler-enforced constraints where they buy stronger guarantees.

Both models may ask developers to learn something new. They spend that complexity differently.

Ryan's garbage-collector analogy

Ryan offers one useful analogy for why he thinks runtime knowledge will remain important.

Garbage collectors work from the live object graph: which objects are actually reachable is ultimately a runtime fact. Static analysis can optimize memory management, but it generally supplements rather than replaces runtime knowledge. Rust gets much further statically by imposing a stricter language, yet still uses runtime mechanisms where the compiler cannot prove enough.8

Ryan's bet is that reactivity will settle into a similar balance:

Use static analysis where it can prove something; keep runtime knowledge for relationships that only become clear while the program executes.

This is an analogy, not proof that UI frameworks must work like memory management. But it explains why Ryan sees Solid's live graph as valuable information rather than machinery that a sufficiently good compiler should eventually eliminate.

Dominic accepts the Rust comparison more than he rejects it. He sees "use strong" as exactly the sort of stricter language that could let Octane prove more.

So even here, the disagreement is mostly about how far that static side can productively go.

AI agents cut both ways

Both creators think agents make stricter programming models easier to adopt.

Dominic's case: agents can obey compiler rules, perform large mechanical refactors, and even debug transformed compiler output without caring that it looks different from the source.9

Ryan's case: agents also reduce the importance of familiar day-one syntax and make rich runtime information more valuable. When debugging, an agent wants answers to questions such as “why is this pending?” or “what caused this update?”10

Neither argument defeats the other.

Agents make stronger static constraints cheaper, and they make deeper runtime introspection more usable.

This part of the debate will probably be settled by real tooling and debugging experience rather than theory.

Alec Larson — the moderator's evolution

Alec's original comparison gave Solid several separate wins that were really consequences of one underlying choice: making a fine-grained reactive graph fundamental. Dominic correctly challenged that, especially the claim that component-oriented rendering implied weaker large-app architecture.11

The thread then improved its central question twice:

  1. Which complexity should the programmer express, and which should the compiler infer?
  2. What knowledge should survive compilation so the running program can explain itself?

Dominic's latest response adds a necessary third piece:

  1. What semantic meaning should ordinary-looking source code carry in the first place?

Together, those three questions capture the debate much better than the original Solid-vs-Octane scorecard.

Where they actually differ

Solid 2 Octane
Reactive reads Reads establish live dependency relationships. Prefer ordinary render-time calculations where possible.
Sync → async Downstream code should not need to change; readiness is dynamic. Dominic now agrees with that goal.
Compiler Useful on top of explicit runtime semantics. Increasingly responsible for guarantees, optimization, and coordination.
Runtime knowledge Keep the dependency graph itself available. Keep enough runtime evidence to explain behavior without requiring one universal graph.
Debugging bet Inspect the actual graph that produced the behavior. Connect compiler decisions to source-linked runtime instrumentation.
Learning curve Learn a more explicitly reactive model. Keep more familiar concepts, while accepting stronger compiler-enforced rules.

The core question

The thread has converged on a much better disagreement than “signals vs hooks”:

Should reactive relationships themselves be part of the semantics of executing code, or should ordinary JavaScript remain the default model while a compiler establishes stronger guarantees and the runtime retains only the information needed for execution and debugging?

Ryan is betting that the live graph is valuable precisely because it survives into the world where the program actually behaves, breaks, and gets debugged.

Dominic is betting that you can preserve simpler source semantics, let a stricter compiler understand much more of the program, and still retain enough runtime evidence to make the result understandable.

Neither has proved the other wrong.

The async disagreement is now relatively small. Both accept stronger language rules. Both accept the need for runtime evidence. Both think agents change what trade-offs are practical.

What remains is a competition between two ideas about where the framework's deepest semantics should live.

Which model earns the constraints it asks developers—and their agents—to live with?

Footnotes

  1. Dominic Gannaway — latest response. Dominic distinguishes ordinary value execution from tracked reactivity: moving a reactive read, passing its result rather than its getter, or evaluating it outside the relevant tracking scope can change behavior. He contrasts this with Octane's React-shaped default, where calculations inside render run again and memoization is an optimization. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
    Plain English: In Solid, where you ask for a value can help determine what should update later. Dominic sees that as both useful and something the programmer has to understand. ↩2
  2. Ryan Carniato. Ryan argues that Solid's dependency graph exists as a runtime data structure containing current sources, dependents, ownership, pending state, and other causal information. Dependencies reflect reads that actually happened, including conditional ones. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c?permalink_comment_id=6318715#gistcomment-6318715
    Plain English: Solid keeps a live map of “this depends on that,” rather than only reconstructing that map from the source code.
  3. Dominic Gannaway — latest response. Dominic agrees that debugging needs evidence from the running application and says Octane does not yet provide the same causal view as Solid's graph. He points to existing source-linked render causes, hook state, and transition/Suspense state, and argues that compiler decisions can eventually be connected to runtime inspection without making a fine-grained graph the universal model. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
  4. Ryan Carniato — follow-up. Ryan describes Solid 2's colorless async contract: every reactive read may temporarily not be ready, pending-ness propagates through derivation, and boundaries or readiness APIs handle it where necessary. The intended payoff is that changing a source from sync to async does not structurally rewrite downstream code. He also corrected the historical lineage of this design; Dominic accepted the timeline correction. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
    Plain English: A value can become “sometimes delayed” without forcing every user of that value to become visibly async too.
  5. Dominic Gannaway — latest response. Dominic explicitly accepts the value of Solid's colorless sync-to-async substitution and says he is not proposing that Promise or explicit async coloring spread through downstream code. His remaining criticism concerns tracked-read semantics rather than the colorless async goal itself. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
  6. Dominic Gannaway. Dominic presents "use strong" as an experiment in compiler-enforced restrictions, with no override for prohibited patterns. His stated principle is that restrictions should buy useful guarantees and removed patterns should receive better alternatives. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c?permalink_comment_id=6318775#gistcomment-6318775
  7. Dominic Gannaway — latest response. Dominic accepts that strong mode asks developers to learn new rules but argues that React knowledge can still transfer through components, props, state, composition, and application structure. Strong mode is currently opt-in. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
  8. Ryan Carniato — follow-up. Ryan uses garbage collection as an analogy for facts that are naturally known at runtime. He argues that static memory analysis generally supplements a live runtime system, while Rust achieves stronger static guarantees by imposing a stricter language and still uses runtime mechanisms where proofs end. This is Ryan's analogy, not independent proof that reactivity must follow the same path. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
  9. Dominic Gannaway — latest response. Dominic argues that agents make stronger compiler constraints easier to adopt and describes an instance where an agent successfully debugged an Octane application by instrumenting transformed output. The anecdote demonstrates feasibility in that case rather than establishing a general observability advantage. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
  10. Ryan Carniato — follow-up. Ryan agrees that agents make strict compiler contracts more practical, but argues that agents also reduce the value of familiar day-one syntax and increase the usefulness of runtime answers about pending state and update causality. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c
  11. Alec Larson — revised framing. After Dominic's initial critique, Alec accepts that the original scorecard double-counted several benefits of Solid's reactive graph, withdraws architectural scaling as an independent Solid win, and reframes the discussion around explicit reactive semantics versus compiler recovery from ordinary code. https://gist.github.com/aleclarson/0f4266d63fd83c7a5ea5512441bbea0c?permalink_comment_id=6313877#gistcomment-6313877
添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论