<?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: Luciano0322</title>
    <description>The latest articles on DEV Community by Luciano0322 (@luciano0322).</description>
    <link>https://dev.to/luciano0322</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png</url>
      <title>DEV Community: Luciano0322</title>
      <link>https://dev.to/luciano0322</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luciano0322"/>
    <language>en</language>
    <item>
      <title>When Render No Longer Owns the Entire Data Flow</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 22 Jun 2026 09:16:01 +0000</pubDate>
      <link>https://dev.to/luciano0322/when-render-no-longer-owns-the-entire-data-flow-20co</link>
      <guid>https://dev.to/luciano0322/when-render-no-longer-owns-the-entire-data-flow-20co</guid>
      <description>&lt;p&gt;In frontend development, it is very easy to think of data flow and rendering as the same thing. After all, for most applications, the final goal is to display data on the screen. Data comes back from an API, gets written into state, the component re-renders, and the UI updates.&lt;/p&gt;

&lt;p&gt;This path is so familiar that we often unconsciously assume:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The purpose of data changes is to trigger render.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But as an application scales, this assumption begins to crack. Data flow does not exist exclusively to serve the UI. In a complex system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some data changes simply update derived state.&lt;/li&gt;
&lt;li&gt;Some trigger background tasks.&lt;/li&gt;
&lt;li&gt;Some synchronize local caches.&lt;/li&gt;
&lt;li&gt;Some orchestrate async resources.&lt;/li&gt;
&lt;li&gt;Some happen entirely under the hood, with no need for the UI to ever know.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When every piece of data is forced to route back through a component—tangled up in render lifecycles, hooks, watchers, or effects—the architecture inevitably turns into a mess. This isn’t a flaw in React or Vue. It happens because, fundamentally, render was never meant to own the entire data flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Render Is a Consumer of Data Flow, Not the Owner
&lt;/h2&gt;

&lt;p&gt;Over time, I've come to deeply embrace this mindset:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Render is just one side effect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This sentence deeply changed the way I think about frontend architecture, because it reframes the core problem.&lt;/p&gt;

&lt;p&gt;Render is, of course, extremely important. Without render, there is no visible output for the user. But from the perspective of a reactive system, render should never be the center of the data flow. It is only one possible side effect produced by data changes.&lt;/p&gt;

&lt;p&gt;When a piece of data changes, the system may trigger a chain of reactions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source state changes
        ↓
derived state updates
        ↓
effects run
        ↓
render may happen
        ↓
async work may continue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Render is one result of the system.&lt;br&gt;
It is not the owner of the system.&lt;/p&gt;

&lt;p&gt;However, many frontend architectures and usage patterns make us unconsciously treat render as the ownership boundary of data flow.&lt;/p&gt;

&lt;p&gt;For example, in React, we often store data inside component state, then use &lt;code&gt;useMemo&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, and &lt;code&gt;useCallback&lt;/code&gt; to manage derived data and side effects.&lt;/p&gt;

&lt;p&gt;In Vue, the reactivity system is more fine-grained, but if the application’s data flow still revolves heavily around component scope, &lt;code&gt;watch&lt;/code&gt;, &lt;code&gt;watchEffect&lt;/code&gt;, or template consumption, data ownership can still become scattered.&lt;/p&gt;

&lt;p&gt;This is not about whether a framework is capable or not.&lt;/p&gt;

&lt;p&gt;It is about where the center of architectural thinking is placed.&lt;/p&gt;

&lt;p&gt;Frameworks usually care about this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When should the UI update?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But when I was designing &lt;code&gt;signal-kernel&lt;/code&gt;, the question in my mind was different:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After data changes, who should own the update logic?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These two questions may look similar, but they are fundamentally different.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Components Should Not Carry All Data Flow Responsibilities
&lt;/h2&gt;

&lt;p&gt;Components are great at describing UI. They are good at composing views, receiving props, handling events, and presenting state.&lt;/p&gt;

&lt;p&gt;But they are not ideal ownership boundaries for the entire data flow. This isn't obvious when your state is simple, but once derived state and async state start piling up, components are quickly crushed under responsibilities they were never meant to hold.&lt;/p&gt;

&lt;p&gt;Consider a common flow like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userId changes
  → fetch user profile
  → fetch permissions
  → compute available actions
  → update form visibility
  → trigger validation
  → maybe render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this entire flow is placed inside a component, it often becomes something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useEffect&lt;/code&gt; fetch data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useMemo&lt;/code&gt; compute derived state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useEffect&lt;/code&gt; sync another state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useEffect&lt;/code&gt; trigger validation&lt;/li&gt;
&lt;li&gt;conditional render&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, this may still look acceptable. But as requirements grow, the component slowly turns into a data-flow scheduler.&lt;/p&gt;

&lt;p&gt;It no longer only describes the UI. It also has to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who depends on whom?&lt;/li&gt;
&lt;li&gt;Who should update first?&lt;/li&gt;
&lt;li&gt;Which values can be recomputed?&lt;/li&gt;
&lt;li&gt;Which side effects are allowed to run?&lt;/li&gt;
&lt;li&gt;Which async work needs to be cancelled?&lt;/li&gt;
&lt;li&gt;Which state must not drift?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, the responsibility of the component has already gone far beyond render. And the most troublesome part is that these dependencies are usually not explicit. They are hidden inside hooks, watchers, callbacks, and dependency arrays.&lt;/p&gt;

&lt;p&gt;The system may still work on the surface, but the ownership of the data flow has been shattered.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Boundary Problem of Derived State
&lt;/h2&gt;

&lt;p&gt;Derived state is where this architectural leak is most glaring. By derived state, I simply mean data that can be programmatically inferred from other data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cartItems&lt;/code&gt; → &lt;code&gt;totalPrice&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;userRole&lt;/code&gt; + &lt;code&gt;permissions&lt;/code&gt; → &lt;code&gt;availableActions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;searchKeyword&lt;/code&gt; + &lt;code&gt;filters&lt;/code&gt; + &lt;code&gt;rawList&lt;/code&gt; → &lt;code&gt;visibleList&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ideally, these are just nodes in a reactive graph. When source state changes, the derived state naturally marks itself as stale and recomputes only when necessary.&lt;/p&gt;

&lt;p&gt;But in a render-centric codebase, these derivations are trapped inside components through &lt;code&gt;useMemo&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, or similar component-scoped mechanisms. This is fine in isolation. This is fine in isolation. The ownership boundary starts to blur, however, when that derived state is needed by an async task, a cache, or a business rule outside the UI layer.&lt;/p&gt;

&lt;p&gt;Suddenly, state that &lt;em&gt;looks&lt;/em&gt; like it belongs to the UI is actually carrying the weight of domain logic. This is the exact ambiguity of ownership I keep emphasizing.&lt;/p&gt;




&lt;h2&gt;
  
  
  It's About Ownership, Not Performance
&lt;/h2&gt;

&lt;p&gt;When people talk about signals or fine-grained reactivity, the first thing that usually comes to mind is performance.React re-renders; Solid updates the DOM directly; Vue tracks dependencies precisely; Signals bypass unnecessary recomputations.&lt;/p&gt;

&lt;p&gt;These are real, practical benefits. But over time, I’ve realized that performance is just the most visible byproduct. The actual paradigm shift is ownership.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Render-centric model&lt;/strong&gt;: The UI owns the data flow. Data mutations must pass through the UI lifecycle (hooks, component scopes) before they can be organized, derived, and synchronized.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph-centric model&lt;/strong&gt;: The reactive graph owns the data flow. Events trigger signals, signals derive a computed graph, and effects, resources, and renders are all simply downstream consumers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This realization became my North Star while building signal-kernel. I wasn't trying to write another UI framework, nor was I trying to prove React or Vue wrong. I just wanted to answer one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If render is no longer the center of data flow, who should take over?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My answer is &lt;strong&gt;the reactive graph&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let Render Return to Its Proper Position
&lt;/h2&gt;

&lt;p&gt;When the reactive graph takes over, render is drastically simplified. It doesn't need to know how data is derived, it doesn't orchestrate update orders, and it doesn't juggle async consistency. It just reads the state it cares about and paints the screen.&lt;/p&gt;

&lt;p&gt;This isn't about downplaying the importance of the UI. It’s about letting the UI return to what it's actually good at. In this model, the boundaries are strict:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Signal&lt;/strong&gt; owns the source state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computed&lt;/strong&gt; owns the derived state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource&lt;/strong&gt; owns the async state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effect&lt;/strong&gt; owns the side effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; owns the visual output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By extracting data flow from the render lifecycle, we give data its own independent ownership boundary. This is the core semantic philosophy behind &lt;code&gt;signal-kernel&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means for React and Vue
&lt;/h2&gt;

&lt;p&gt;For React developers, this pain point is acute. Because React is fundamentally render-driven, data-flow issues inevitably turn into interrogations about &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useMemo&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, dependency arrays, and re-render optimization. This is exactly why large React codebases suffer from dependency array fatigue, stale closures, and effect overuse.&lt;/p&gt;

&lt;p&gt;For Vue, the situation is more nuanced. Vue already has a built-in reactivity system; &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, and &lt;code&gt;watch&lt;/code&gt; describe data dependencies beautifully. A Vue developer reading this might think: "&lt;em&gt;Isn't Vue doing this already?&lt;/em&gt;"&lt;/p&gt;

&lt;p&gt;To an extent, yes. Vue brought reactive graphs into mainstream application development much earlier. But there is a key distinction: Vue’s reactivity can be used independently, but its primary design context is still the Vue application model.&lt;/p&gt;

&lt;p&gt;What signal-kernel explores is a framework-agnostic reactive graph. I'm not asking, "&lt;em&gt;Should we use signals to update Vue components?&lt;/em&gt;" I'm asking, "&lt;em&gt;Can we build a standalone data core that React, Vue, background jobs, async runtimes, and even AI agents can all consume simultaneously?&lt;/em&gt;"&lt;/p&gt;

&lt;p&gt;That’s why the React and Vue adapters for signal-kernel are intentionally thin. A framework adapter shouldn't seize control of the data flow; it should merely bridge the graph to the UI.&lt;/p&gt;




&lt;h2&gt;
  
  
  A System You Can Actually Reason About
&lt;/h2&gt;

&lt;p&gt;When render relinquishes control, you don't just get better performance—you get a system you can actually reason about. You stop asking framework-specific syntax questions and start asking architectural ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this source state or derived state?&lt;/li&gt;
&lt;li&gt;Is this a synchronous derivation or an async result?&lt;/li&gt;
&lt;li&gt;Is this a data-layer effect or a UI-layer effect?&lt;/li&gt;
&lt;li&gt;Should this state live inside the component, or inside an independent reactive graph?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conversely, when everything is pushed back into the component lifecycle, profound architectural flaws are disguised as hook usage questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do I write this dependency array?&lt;/li&gt;
&lt;li&gt;Why is this &lt;code&gt;useEffect&lt;/code&gt; firing twice?&lt;/li&gt;
&lt;li&gt;Should I wrap this in &lt;code&gt;useMemo&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Is &lt;code&gt;watchEffect&lt;/code&gt; too automatic?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions all have technical answers, but they mask the deeper, underlying problem: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who is actually supposed to own this piece of the data flow?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Downgrading Render Into One of Many Outputs
&lt;/h2&gt;

&lt;p&gt;UI frameworks are incredibly important. React, Vue, and Solid have all solved very difficult problems in their own dimensions.&lt;/p&gt;

&lt;p&gt;But in complex frontend applications, we need to recognize one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Render should no longer be treated as the only center of data flow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Only when data flow has its own ownership can a system move from UI-driven state management toward graph-driven state ownership.&lt;/p&gt;

&lt;p&gt;On the surface, &lt;a href="https://github.com/Luciano0322/signal-kernel" rel="noopener noreferrer"&gt;&lt;code&gt;signal-kernel&lt;/code&gt;&lt;/a&gt; is a signal library. But at its core, it is an exploration of how frontend data architecture can be rebuilt when render steps back and becomes just one kind of side effect.&lt;/p&gt;

&lt;p&gt;In the next article, I will continue breaking down this problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When state, derived state, and effect are mixed together, how does a system gradually lose its boundaries?&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>architecture</category>
      <category>frontend</category>
    </item>
    <item>
      <title>From Signals to Ownership: Why I Built a Dataflow Kernel</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 15 Jun 2026 07:17:57 +0000</pubDate>
      <link>https://dev.to/luciano0322/from-signals-to-ownership-why-i-built-a-dataflow-kernel-1h0f</link>
      <guid>https://dev.to/luciano0322/from-signals-to-ownership-why-i-built-a-dataflow-kernel-1h0f</guid>
      <description>&lt;p&gt;When I first started building &lt;code&gt;signal-kernel&lt;/code&gt;, I thought I was simply writing a signal library—a fine-grained reactive system not tied to any specific UI framework. On the surface, it had all the basic building blocks you would expect from a signal library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core primitives:&lt;/strong&gt; &lt;code&gt;Signal&lt;/code&gt;, &lt;code&gt;Computed&lt;/code&gt;, and &lt;code&gt;Effect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mechanisms:&lt;/strong&gt; dependency tracking and scheduling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensions:&lt;/strong&gt; async resources and framework adapters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It could build a reactive graph, track dependencies, update derived state precisely when data changed, and bridge the result into React or Vue. But the deeper I dug into the implementation, the more I realized the real problem wasn't about building another signal library. The core question was actually this: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In a complex application, who should truly own the dataflow?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Fragmented Dataflow and the Derived State Problem
&lt;/h2&gt;

&lt;p&gt;In modern frontend applications, dataflow often feels natural at first. API data comes back, gets written into state, and components render the result. If we need derived data, we use &lt;code&gt;useMemo&lt;/code&gt;. If we need side effects, we use &lt;code&gt;useEffect&lt;/code&gt;. If we need async requests, we use a query library, and if we need shared state, we introduce a store.&lt;/p&gt;

&lt;p&gt;Individually, none of these solutions are wrong. React is not wrong. Vue is not wrong. TanStack Query is not wrong. Zustand and Jotai are not wrong either. The real problem appears when the system grows larger. At that point, the exact same logical dataflow starts getting split across multiple ownership models.&lt;/p&gt;

&lt;p&gt;Some state belongs to components, while other state belongs to a store. Some derived state is trapped inside the render phase, and async state lives inside a query cache. The system still works, but the boundaries of the dataflow start to depend heavily on conventions, team discipline, and developer memory. It becomes incredibly hard to answer one seemingly simple question: &lt;strong&gt;Who actually owns this derived state?&lt;/strong&gt; Is it the component, the store, the framework runtime, or just a temporary value calculated inside a hook?&lt;/p&gt;

&lt;h2&gt;
  
  
  Render Should Not Naturally Own the Entire Dataflow
&lt;/h2&gt;

&lt;p&gt;Frontend developers often treat render as the center of the system. This is completely understandable—what we look at and debug every day is the UI. So when data changes, our first instinct is usually: &lt;em&gt;"Which component needs to re-render?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But if we reverse the question, the architecture looks very different. When data changes, perhaps the first question should be: &lt;em&gt;"Which pieces of derived data are actually affected?"&lt;/em&gt; Render is simply one kind of side effect caused by data changes. It is extremely important, but it should not naturally own the entire dataflow.&lt;/p&gt;

&lt;p&gt;This became one of the core architectural lessons I learned: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The reactive graph should first describe the relationships between data, while render should only be one consumer of that graph.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  From Signal Library to Dataflow Kernel
&lt;/h2&gt;

&lt;p&gt;If I only saw &lt;code&gt;signal-kernel&lt;/code&gt; as a signal library, then most design questions would stay at the API surface: Should it feel more like Solid? Should computed values be lazy or eager? Should it support batching? These are important, but they hide the deeper issue: When a piece of state changes, who is responsible for deciding which derived states are affected?&lt;/p&gt;

&lt;p&gt;In a traditional component-driven model, this usually happens during render:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Data changes → component re-executes → derived data is recalculated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This model works beautifully for small applications, but it forces the lifecycle of derived state to follow the lifecycle of render. Once derived state becomes expensive, needs to be shared across frameworks, or needs to be deeply connected with async streams, this model becomes fragile.&lt;/p&gt;

&lt;p&gt;That is why the real goal of &lt;code&gt;signal-kernel&lt;/code&gt; was never simply to “reduce re-renders.” The goal was to &lt;strong&gt;detach derived dataflow from render ownership&lt;/strong&gt;. What I actually wanted to build was a dataflow kernel.&lt;/p&gt;

&lt;p&gt;A system that focuses on lower-level architectural concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How state becomes a pure source of truth.&lt;/li&gt;
&lt;li&gt;How derived state is tracked stably inside a reactive graph.&lt;/li&gt;
&lt;li&gt;How effects can be separated cleanly from the render lifecycle.&lt;/li&gt;
&lt;li&gt;How async work and continuously written streams can become part of the reactive graph while keeping clear state boundaries.&lt;/li&gt;
&lt;li&gt;How render can return to its proper role: a consumer, not the owner.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keeping the Boundary: Thin Framework Adapters
&lt;/h2&gt;

&lt;p&gt;This way of thinking also directly shaped how I designed the React and Vue adapters. At the beginning, I naturally wanted to provide many convenient hooks and APIs that felt familiar to framework users. But over time, I became much more conservative.&lt;/p&gt;

&lt;p&gt;If an adapter becomes too thick, it quietly pulls ownership back into the UI framework. And once that happens, the data independence that &lt;code&gt;signal-kernel&lt;/code&gt; is trying to preserve starts to fade away.&lt;/p&gt;

&lt;p&gt;A cleaner and more stable boundary should look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core owns the reactive graph.&lt;/li&gt;
&lt;li&gt;Async-runtime owns the consistency of async state.&lt;/li&gt;
&lt;li&gt;Framework adapters only subscribe to and read snapshots.&lt;/li&gt;
&lt;li&gt;Render only draws the current result to the screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An adapter should not redefine the dataflow. It should only be a transparent pipe into the framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rethinking Framework Responsibility
&lt;/h2&gt;

&lt;p&gt;I want to make one thing clear: this series is not meant to criticize React or any other UI tool. React is still excellent at handling UI consistency, and TanStack Query solves a very real, difficult server-state problem.&lt;/p&gt;

&lt;p&gt;What I want to explore is a deeper architectural question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When we habitually put all data logic into the model of a UI framework, are we accidentally making render responsible for too much?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If reactive dataflow itself can be an independent system, then what role should a UI framework actually play?&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Was Really Studying Was Ownership
&lt;/h2&gt;

&lt;p&gt;While implementing the core and async-runtime of &lt;code&gt;signal-kernel&lt;/code&gt;, I kept running into a series of uncomfortable architectural questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After a Promise resolves, is it really just a simple &lt;code&gt;setState&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;If a stream keeps emitting chunks, who should own its stable value, status, and error?&lt;/li&gt;
&lt;li&gt;After a mutation succeeds, is invalidation the responsibility of the query cache, or the reactive graph?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these questions eventually point to the same word: &lt;strong&gt;Ownership.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I originally thought I was building a signal library. But the more I built, the more I realized that what I was really studying was how the boundaries of dataflow should be redefined in increasingly complex frontend applications. APIs can change. Implementations can be replaced. But the core question remains the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When data changes, who is responsible for understanding that change?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is always render, then we are forced to keep layering workarounds around the UI framework. But if the answer is the reactive graph, then render can return to its most reasonable position. It is one outlet of the dataflow. It is one side effect. It is a consumer of the system, not the owner.&lt;/p&gt;

&lt;p&gt;That is the most important lesson I learned while building &lt;a href="https://github.com/Luciano0322/signal-kernel" rel="noopener noreferrer"&gt;&lt;code&gt;signal-kernel&lt;/code&gt;&lt;/a&gt;. If this idea resonates with you, I’m exploring it through &lt;a href="https://github.com/Luciano0322/signal-kernel" rel="noopener noreferrer"&gt;&lt;code&gt;signal-kernel&lt;/code&gt;&lt;/a&gt;, a framework-agnostic reactive dataflow kernel.&lt;/p&gt;

&lt;p&gt;The project is still evolving, but its direction is clear: &lt;strong&gt;render should be a consumer of the dataflow, not the owner.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the next article, I want to continue exploring this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What actually changes in frontend architecture when render no longer owns the entire dataflow?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>What I Learned After Building a Signal System from Scratch</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 08 Jun 2026 02:27:19 +0000</pubDate>
      <link>https://dev.to/luciano0322/what-i-learned-after-building-a-signal-system-from-scratch-280p</link>
      <guid>https://dev.to/luciano0322/what-i-learned-after-building-a-signal-system-from-scratch-280p</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;At this point, this series on Signals and fine-grained reactivity is temporarily coming to an end.&lt;/p&gt;

&lt;p&gt;This article will not introduce new technical details. Instead, I want to share some personal reflections and thoughts after writing the whole series.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Influence of Ryan Carniato
&lt;/h1&gt;

&lt;p&gt;In some sense, this entire series was inspired by Ryan Carniato, the creator of Solid.js.&lt;/p&gt;

&lt;p&gt;Ryan has always been able to point directly at the pain points we face in React development. As a senior React engineer, I often find myself thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes, this is exactly the problem I run into every day.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Solid.js is undoubtedly an excellent framework, although its learning curve can be high for beginners.&lt;/p&gt;

&lt;p&gt;However, from my perspective, many of its design choices feel more reasonable than the direction React has taken. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Effects without dependency arrays&lt;/li&gt;
&lt;li&gt;Separating mounted logic from cleanup logic&lt;/li&gt;
&lt;li&gt;JSX without the Virtual DOM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all things that many developers in the React community had asked for before, but they were never truly prioritized.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Dilemma of React and Next.js
&lt;/h1&gt;

&lt;p&gt;Over the past few years, React’s direction has become increasingly influenced by the needs of the Next.js ecosystem.&lt;/p&gt;

&lt;p&gt;Server Components introduced an important architectural shift, but they also forced many frontend engineers to adopt a model that still feels immature in everyday application development.&lt;/p&gt;

&lt;p&gt;To be clear, I am not saying Server Components are useless. They solve real problems around server-side data access, streaming, and reducing client-side JavaScript. But from a frontend developer’s perspective, they also make the mental model of React significantly more complex.&lt;/p&gt;

&lt;p&gt;To be honest, I sometimes miss the era of “Next.js without Server Components.” React was simpler back then. Its problems were obvious, but at least the boundaries were easier to reason about, and we had more freedom to choose our own solutions.&lt;/p&gt;

&lt;p&gt;People in the community have proposed ideas such as “Should React introduce signals?” or even “Should React explore a version without the Virtual DOM?” These ideas may not align naturally with the current product direction and incentives around React and Next.js, but I still think they are worth discussing seriously.&lt;/p&gt;

&lt;p&gt;My point is not that React is wrong or that the Virtual DOM is useless.&lt;/p&gt;

&lt;p&gt;The Virtual DOM is still a reasonable abstraction in many scenarios. For example, in cross-platform environments such as React Native, it provides a practical compromise between a declarative UI model and multiple rendering targets.&lt;/p&gt;

&lt;p&gt;My point is narrower:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Virtual DOM is not wrong. It is just no longer the only reasonable center of frontend architecture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the problem is fine-grained data dependency, update precision, and scheduler correctness, signals provide a different model that deserves serious consideration.&lt;/p&gt;

&lt;p&gt;What I see today is a React ecosystem entering a more conflicted phase. Although projects like TanStack are still doing excellent work to improve the developer experience, if a mature alternative appears in the future, I may no longer choose Next.js by default.&lt;/p&gt;

&lt;h1&gt;
  
  
  Common Misunderstandings
&lt;/h1&gt;

&lt;h2&gt;
  
  
  “Isn’t this just Jotai?”
&lt;/h2&gt;

&lt;p&gt;In the community, you may sometimes hear people say that Jotai — or the atomic state model in general — is basically the same thing as signals. Some even argue that because it is built closer to the framework ecosystem, it is more developer-friendly.&lt;/p&gt;

&lt;p&gt;But if you have followed this series and implemented the concepts step by step, or if you have ever implemented a reactive system yourself, you will understand that the two approaches are based on very different design philosophies.&lt;/p&gt;

&lt;p&gt;The key difference is whether the system handles dependencies.&lt;/p&gt;

&lt;p&gt;Below is a simple conceptual comparison.&lt;/p&gt;

&lt;h3&gt;
  
  
  Atomic State: The Recoil / Jotai Family
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core idea&lt;/strong&gt;: Split state into the smallest possible units, often called state atoms. There is no implicit dependency tracking between them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition model&lt;/strong&gt;: Use selectors or derived functions to organize logic and compose state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update model&lt;/strong&gt;: When state changes, the system does not automatically notify downstream dependents. Instead, selectors are re-executed as pure functions.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Characteristics&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to understand in a functional way: state is a value, and a selector is a pure function.&lt;/li&gt;
&lt;li&gt;However, the dependency graph is not explicit. Every read may require recomputation, which can lead to repeated calculations.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Signals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core idea&lt;/strong&gt;: Signals are also fine-grained state units, but they include an explicit dependency-tracking graph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition model&lt;/strong&gt;: &lt;code&gt;computed&lt;/code&gt; and &lt;code&gt;effect&lt;/code&gt; automatically register dependencies during execution. When a source value changes later, downstream subscribers are notified automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update model&lt;/strong&gt;: Usually push-based. Once a source value changes, downstream nodes are marked as stale, and the scheduler decides when to run updates.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Characteristics&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoids unnecessary recomputation because the runtime knows who depends on whom.&lt;/li&gt;
&lt;li&gt;Update propagation is faster and more precise.&lt;/li&gt;
&lt;li&gt;Requires the runtime to manage a dependency graph.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary of the Difference
&lt;/h3&gt;

&lt;p&gt;In one sentence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Atomic state&lt;/strong&gt;: Splits state, but does not deeply manage dependencies. The system needs to “calculate again” through functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signals&lt;/strong&gt;: Splits state and adds dependency tracking. The system knows who needs to update.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not handling dependencies does make the model easier to write and understand. Jotai also relies on the framework’s own state model, which naturally aligns with the framework lifecycle and avoids many synchronization problems.&lt;/p&gt;

&lt;p&gt;But the trade-off is that this model must rely on framework re-renders to guarantee correctness. Signals, on the other hand, can achieve more fine-grained updates through runtime-level dependency tracking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flow Difference
&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%2Fz9p9jrgpua95tnn7yzqe.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%2Fz9p9jrgpua95tnn7yzqe.png" alt="Atomic state vs signal flow" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  “Isn’t this just Dependency Injection?”
&lt;/h2&gt;

&lt;p&gt;Another common comparison is to treat signals, especially when integrated with Vue or React, as something similar to Dependency Injection (DI) in the traditional OOP world.&lt;/p&gt;

&lt;p&gt;At first glance, it does look somewhat similar: you inject external state into a framework and use it inside the application.&lt;/p&gt;

&lt;p&gt;But the essential difference is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DI solves the composition and management of &lt;strong&gt;object dependencies&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Signals solve the tracking and update timing of &lt;strong&gt;data dependencies&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;They may look similar on the surface, but they solve problems at completely different dimensions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What Does DI Solve?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It answers: “Which object needs which service?”&lt;/li&gt;
&lt;li&gt;Timing: Construction time&lt;/li&gt;
&lt;li&gt;Keywords: dependency relationship, object composition&lt;/li&gt;
&lt;li&gt;Example: A &lt;code&gt;Car&lt;/code&gt; needs an &lt;code&gt;Engine&lt;/code&gt;, and the DI framework injects it for you.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Engine&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;h3&gt;
  
  
  What Do Signals Solve?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;They answer: “Which piece of data depends on which other piece of data?”&lt;/li&gt;
&lt;li&gt;Timing: Runtime&lt;/li&gt;
&lt;li&gt;Keywords: data tracking, state update propagation&lt;/li&gt;
&lt;li&gt;Example: &lt;code&gt;totalPrice&lt;/code&gt; depends on &lt;code&gt;count * unitPrice&lt;/code&gt;, and it updates automatically when the source values change.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unitPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;unitPrice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Simple Clarification
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;DI helps objects get the services they need.&lt;/li&gt;
&lt;li&gt;Signals help the system know who should update when data changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  “Signals are just another state management tool”
&lt;/h2&gt;

&lt;p&gt;Many people think signals are just another form of state management.&lt;/p&gt;

&lt;p&gt;But after going through the implementation details in this series, you can see that signals touch much more than state management.&lt;/p&gt;

&lt;p&gt;A state management library usually helps you organize where state lives and how it is updated.&lt;/p&gt;

&lt;p&gt;A signal system goes one layer deeper. It asks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How are data dependencies tracked?&lt;/li&gt;
&lt;li&gt;How are invalidations propagated?&lt;/li&gt;
&lt;li&gt;When should derived values recompute?&lt;/li&gt;
&lt;li&gt;When should effects run?&lt;/li&gt;
&lt;li&gt;How should the scheduler preserve consistency?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why reducing signals to “just another state management tool” misses the point.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What signals challenge is not merely state management, but the long-standing assumption that the Virtual DOM must sit at the center of frontend architecture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That does not mean the Virtual DOM has no value. It means we should treat it as one architectural trade-off among many, not as the only valid mental model for UI development.&lt;/p&gt;

&lt;p&gt;Technical evolution is never just about code. It is also about incentives, existing knowledge systems, and the cost of changing how people think.&lt;/p&gt;

&lt;p&gt;Signals are uncomfortable because they move the discussion away from component re-rendering and toward data dependency itself.&lt;/p&gt;

&lt;h1&gt;
  
  
  Adjusting Your Mindset: Facing Irrational Criticism
&lt;/h1&gt;

&lt;p&gt;Some people will directly deny the value of signals. Some may even mock them with comments like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Signals are useless.&lt;/p&gt;

&lt;p&gt;Dan already told you not to use them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These comments often carry more emotion or tribal position than technical understanding.&lt;/p&gt;

&lt;p&gt;To me, these voices are not a threat. They are more like a mirror:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you already understand the difference between atomic state and signals, you can easily see the blind spots in the argument.&lt;/li&gt;
&lt;li&gt;If you do not understand it yet, then it is a useful reminder that you should study the topic more deeply.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, this does not mean we should blindly reject React’s design decisions either. React has solved many real engineering problems, and its ecosystem is still extremely valuable.&lt;/p&gt;

&lt;p&gt;But no framework author, no matter how experienced or influential, should become a reason for us to stop thinking.&lt;/p&gt;

&lt;p&gt;The healthier attitude is to return to concrete examples, implementation details, and trade-offs.&lt;/p&gt;

&lt;p&gt;So when you encounter this kind of criticism, the best response is not to rush into an argument.&lt;/p&gt;

&lt;p&gt;Return to the code. Return to the design principles.&lt;/p&gt;

&lt;p&gt;Once you can explain the difference in dependency tracking with a simple example, the mockery naturally loses its power.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Future of Signals
&lt;/h1&gt;

&lt;p&gt;Overall, I am quite optimistic about the future of signals.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/tc39/proposal-signals" rel="noopener noreferrer"&gt;TC39 Signals proposal&lt;/a&gt; from last year already shows that signals are becoming increasingly important in frontend development.&lt;/p&gt;

&lt;p&gt;Apart from React, which still seems to maintain a “React knows best” atmosphere, most mainstream frameworks have already entered a new era with signal-like ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue’s Vapor Mode&lt;/li&gt;
&lt;li&gt;Angular 17+ Signals&lt;/li&gt;
&lt;li&gt;Svelte Runes&lt;/li&gt;
&lt;li&gt;Preact Signals&lt;/li&gt;
&lt;li&gt;SolidJS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of them are even moving toward removing the unnecessary parts of the Virtual DOM, such as Vue’s Vapor Mode and SolidJS’s JSX compilation model.&lt;/p&gt;

&lt;p&gt;This means that developers who truly understand the core ideas behind signals will be much more comfortable in future frontend applications.&lt;/p&gt;

&lt;p&gt;In my own experience, once I understood these concepts, switching between different frameworks became much easier.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why I Started This Series
&lt;/h1&gt;

&lt;p&gt;The reason I started this series was that I was building my own open-source signal system library: &lt;a href="https://www.npmjs.com/package/@signal-kernel/core" rel="noopener noreferrer"&gt;signal-kernel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During that process, I kept running into challenges. Then I realized something important:&lt;/p&gt;

&lt;p&gt;Many of the “algorithms and data structures” we learned in school or practiced for interviews are actually useful here.&lt;/p&gt;

&lt;p&gt;This made me even more convinced of one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to design the foundation of a framework well, you must understand JavaScript fundamentals and computer science.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  What I Learned
&lt;/h1&gt;

&lt;p&gt;To me, this series is not just a set of technical notes. It is also part of my own journey as an engineer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It started from my frustration with React.&lt;/li&gt;
&lt;li&gt;It was inspired by Ryan Carniato’s ideas.&lt;/li&gt;
&lt;li&gt;It eventually led me back to the importance of JavaScript fundamentals and algorithms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest lesson I learned is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Frameworks will come and go, but fundamental knowledge and mental models are what allow you to go further.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This series only peels back the first layer of a framework engine: the reactive core.&lt;/p&gt;

&lt;p&gt;Of course, the reactive core is also one of the most important foundations of any framework. If we continue going deeper, we will run into topics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DOM rendering&lt;/li&gt;
&lt;li&gt;UI rendering schedulers&lt;/li&gt;
&lt;li&gt;Component models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every framework makes different decisions and trade-offs in these areas.&lt;/p&gt;

&lt;p&gt;These may become directions for my future research.&lt;/p&gt;

&lt;h1&gt;
  
  
  Advice for Readers
&lt;/h1&gt;

&lt;p&gt;If you are currently learning React, you do not need to rush into Solid.js or any other framework.&lt;/p&gt;

&lt;p&gt;The truly important thing is not which framework you choose. The important thing is understanding the underlying concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The data flow from state → derivation → effect&lt;/li&gt;
&lt;li&gt;Why schedulers and dependency graph management matter&lt;/li&gt;
&lt;li&gt;The difference between push and pull&lt;/li&gt;
&lt;li&gt;The difference between eager and lazy execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you understand these ideas, you can quickly find your direction in any framework.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not just learn how to use a framework. Learn the logic underneath it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Trends will change, but fundamental concepts are the real assets that engineers can carry with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal Implementation Reference
&lt;/h2&gt;

&lt;p&gt;As usual, I will also attach the implementation code from this series for reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Luciano0322/reactivity_lessons" rel="noopener noreferrer"&gt;reactivity_lessons&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository includes unit tests for each section, although only the parts that changed in each chapter are tested.&lt;/p&gt;

&lt;p&gt;I still hope readers will modify the examples themselves instead of simply copying and pasting the code.&lt;/p&gt;

&lt;p&gt;If you only copy the implementation directly, you will miss many opportunities to think through the details.&lt;/p&gt;

&lt;p&gt;The examples I provide are intentionally designed choices. In many cases, the data structures and subscription model may not fit every development scenario. However, they make the concepts easier for beginners to understand.&lt;/p&gt;

&lt;p&gt;I did not want to switch between too many algorithms and data structures during the explanation, because that would introduce unnecessary confusion.&lt;/p&gt;

&lt;p&gt;For more advanced discussion, I recommend focusing on Ryan Carniato’s article series, especially &lt;a href="https://dev.to/this-is-learning/derivations-in-reactivity-4fo1"&gt;Derivations in Reactivity&lt;/a&gt;, which explains many of the trade-offs behind today’s mainstream signal implementations.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>New article published: Building Reactive DevTools
A reactive system should not be a black box.
In this article, I explore how to make a signal-based runtime observable through node inspection, graph visualization, render counters, and hotspot tracking.</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 01 Jun 2026 06:06:33 +0000</pubDate>
      <link>https://dev.to/luciano0322/new-article-published-building-reactive-devtools-a-reactive-system-should-not-be-a-black-box-in-l3m</link>
      <guid>https://dev.to/luciano0322/new-article-published-building-reactive-devtools-a-reactive-system-should-not-be-a-black-box-in-l3m</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/luciano0322/building-reactive-devtools-inspecting-visualizing-and-profiling-the-graph-270e" class="crayons-story__hidden-navigation-link"&gt;Building Reactive DevTools: Inspecting, Visualizing, and Profiling the Graph&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/luciano0322" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" alt="luciano0322 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/luciano0322" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Luciano0322
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Luciano0322
                
              
              &lt;div id="story-author-preview-content-3692092" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/luciano0322" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Luciano0322&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/luciano0322/building-reactive-devtools-inspecting-visualizing-and-profiling-the-graph-270e" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 1&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/luciano0322/building-reactive-devtools-inspecting-visualizing-and-profiling-the-graph-270e" id="article-link-3692092"&gt;
          Building Reactive DevTools: Inspecting, Visualizing, and Profiling the Graph
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devtool"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devtool&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/luciano0322/building-reactive-devtools-inspecting-visualizing-and-profiling-the-graph-270e" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/luciano0322/building-reactive-devtools-inspecting-visualizing-and-profiling-the-graph-270e#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>performance</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building Reactive DevTools: Inspecting, Visualizing, and Profiling the Graph</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 01 Jun 2026 05:59:47 +0000</pubDate>
      <link>https://dev.to/luciano0322/building-reactive-devtools-inspecting-visualizing-and-profiling-the-graph-270e</link>
      <guid>https://dev.to/luciano0322/building-reactive-devtools-inspecting-visualizing-and-profiling-the-graph-270e</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In the previous chapters, we explored:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scheduler internals&lt;/li&gt;
&lt;li&gt;Memory and graph management&lt;/li&gt;
&lt;li&gt;Priority and layered scheduling&lt;/li&gt;
&lt;li&gt;Time-slicing and cooperative scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these mechanisms are essential for making a reactivity system work correctly internally.&lt;/p&gt;

&lt;p&gt;But internal correctness alone is not enough.&lt;/p&gt;

&lt;p&gt;For developers, the more important question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do we observe, debug, and understand the system?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where DevTools and diagnostics become critical.&lt;/p&gt;




&lt;h1&gt;
  
  
  Inspecting Nodes
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Why do we need &lt;code&gt;inspect()&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;One of the most common debugging needs during development is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What is the current value of this signal or computed?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the only solution is &lt;code&gt;console.log&lt;/code&gt;, debugging quickly becomes inconvenient and intrusive.&lt;/p&gt;

&lt;p&gt;A proper inspection layer gives developers visibility without polluting application logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Design
&lt;/h2&gt;

&lt;p&gt;The inspector should provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current &lt;code&gt;value&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dependency relationships (&lt;code&gt;deps / subs&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Whether the node is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disposed&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// devtools.ts&lt;/span&gt;
&lt;span class="c1"&gt;// type reused from previous graph.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Node&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;./graph.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Assign IDs using WeakMap without polluting Node structure&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;seq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;InspectSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kind&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nl"&gt;inDegree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outDegree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kind&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}[];&lt;/span&gt;
  &lt;span class="nl"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kind&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="c1"&gt;// Get a flat snapshot of a single node (non-recursive)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;InspectSnapshot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inDegree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outDegree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="na"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Debug-Friendly Logging
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logInspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;snap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`[inspect] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)  in=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inDegree&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;  out=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outDegree&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  deps ↑&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  deps ↑ (none)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  subs ↓&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  subs ↓ (none)&lt;/span&gt;&lt;span class="dl"&gt;"&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;This provides a much friendlier debugging experience than raw logging.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recursive Graph Inspection
&lt;/h2&gt;

&lt;p&gt;When debugging larger dependency chains, inspecting a single node is often not enough.&lt;/p&gt;

&lt;p&gt;We can recursively expand the graph while avoiding cycles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;inspectRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;dUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;dDown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dDown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}];&lt;/span&gt;

  &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dUp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dDown&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fromId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dUp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fromId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dUp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dDown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dDown&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fromId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dDown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dDown&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="na"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rows&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;h2&gt;
  
  
  Mermaid Export
&lt;/h2&gt;

&lt;p&gt;We can even export subgraphs into Mermaid diagrams for documentation or DevTools visualization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toMermaid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inspectRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&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;graph TD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_#&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;["&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_#&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_#&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&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;h2&gt;
  
  
  API Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;inspect(node)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fastest single-node snapshot&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;logInspect(node)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debug-friendly console tables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;inspectRecursive(node, depth)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small graph expansion without infinite loops&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;toMermaid(node, depth)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export subgraphs for docs or DevTools rendering&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h1&gt;
  
  
  Graph Visualization
&lt;/h1&gt;

&lt;p&gt;As applications grow larger, textual inspection is no longer sufficient.&lt;/p&gt;

&lt;p&gt;At that point, we need dependency graph visualization.&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%2Fjnx4j94p187dkgqwqmm7.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%2Fjnx4j94p187dkgqwqmm7.png" alt="graph visualization" width="765" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signal nodes (blue) represent data sources&lt;/li&gt;
&lt;li&gt;Computed nodes (green) represent derived values&lt;/li&gt;
&lt;li&gt;Effect nodes (yellow) represent side effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside DevTools, we could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click nodes to inspect current values&lt;/li&gt;
&lt;li&gt;Highlight &lt;code&gt;stale&lt;/code&gt; nodes&lt;/li&gt;
&lt;li&gt;Visualize &lt;code&gt;link/unlink&lt;/code&gt; operations&lt;/li&gt;
&lt;li&gt;Observe automatic cleanup and graph pruning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the dataflow fully observable.&lt;/p&gt;

&lt;p&gt;Instead of a black box, developers can literally see:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which state triggered which update.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Render Counters
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In UI frameworks, one of the most common performance issues is over-rendering.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components re-render repeatedly&lt;/li&gt;
&lt;li&gt;But nothing meaningful actually changes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Feature Design
&lt;/h2&gt;

&lt;p&gt;A render counter can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increment on each render&lt;/li&gt;
&lt;li&gt;Display a small overlay badge&lt;/li&gt;
&lt;li&gt;Aggregate render statistics in DevTools&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;This helps developers identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unnecessary re-renders&lt;/li&gt;
&lt;li&gt;Missing memoization&lt;/li&gt;
&lt;li&gt;Poor equality strategies&lt;/li&gt;
&lt;li&gt;Expensive recomputation chains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also guides optimization decisions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;memo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shallowEqual&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;computed caching&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Hotspot Tracking
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Why Track Hotspots?
&lt;/h2&gt;

&lt;p&gt;In large applications, knowing that something updated is not enough.&lt;/p&gt;

&lt;p&gt;We also need to know:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which nodes update the most frequently?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where hotspot profiling becomes valuable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Design
&lt;/h2&gt;

&lt;p&gt;We can track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update counts&lt;/li&gt;
&lt;li&gt;Update frequency&lt;/li&gt;
&lt;li&gt;Execution duration&lt;/li&gt;
&lt;li&gt;Graph degree statistics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And combine them with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heatmaps&lt;/li&gt;
&lt;li&gt;Timeline views&lt;/li&gt;
&lt;li&gt;Priority scheduling traces&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Hotspot Implementation
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;hotspot.ts&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// hotspot.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Node&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;../graph.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;HotspotStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;lastTs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;freqPerMin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;durTotal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;durCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HotspotStats&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;liveNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;?.()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system tracks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequency&lt;/li&gt;
&lt;li&gt;Total execution time&lt;/li&gt;
&lt;li&gt;Average execution time&lt;/li&gt;
&lt;li&gt;Update counts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without modifying the core graph structure itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Tracking Signal Writes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;recordUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We only inject instrumentation where actual work happens.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;signal.set()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;computed.recompute()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;effect.run()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the core runtime clean and minimally invasive.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Wrapping Computation Timing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;withTiming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&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="c1"&gt;// recompute logic&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recomputation frequency&lt;/li&gt;
&lt;li&gt;average execution duration&lt;/li&gt;
&lt;li&gt;expensive reactive chains&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Example Usage
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;logTopHotspots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;freq&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;allNodes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nf"&gt;logTopHotspots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;avgTime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;allNodes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nf"&gt;logTopHotspots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;updates&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;allNodes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets us quickly identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;high-frequency nodes&lt;/li&gt;
&lt;li&gt;expensive computations&lt;/li&gt;
&lt;li&gt;reactive bottlenecks&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Real-World Use Cases
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Game Loops
&lt;/h2&gt;

&lt;p&gt;Identify states updating every frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forms
&lt;/h2&gt;

&lt;p&gt;Detect extremely hot input fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Visualization
&lt;/h2&gt;

&lt;p&gt;Locate nodes responsible for expensive re-renders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async Systems
&lt;/h2&gt;

&lt;p&gt;Observe retry storms or invalidation cascades.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why DevTools Matter
&lt;/h1&gt;

&lt;p&gt;DevTools are not just debugging utilities.&lt;/p&gt;

&lt;p&gt;They also help developers:&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Mental Models
&lt;/h2&gt;

&lt;p&gt;Understand how the reactive graph actually flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize Performance
&lt;/h2&gt;

&lt;p&gt;Locate bottlenecks quickly instead of guessing blindly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn Reactivity
&lt;/h2&gt;

&lt;p&gt;Make invisible runtime behavior visible and intuitive.&lt;/p&gt;




&lt;h1&gt;
  
  
  Possible Future Directions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Timeline Profiling
&lt;/h2&gt;

&lt;p&gt;Visualize update propagation over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Priority-Aware Diagnostics
&lt;/h2&gt;

&lt;p&gt;Inspect how different scheduler priorities interact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated Suggestions
&lt;/h2&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“This signal updates too frequently.”&lt;/li&gt;
&lt;li&gt;“Consider memoizing this computed.”&lt;/li&gt;
&lt;li&gt;“This effect causes excessive downstream invalidation.”&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;By adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;node inspection&lt;/li&gt;
&lt;li&gt;graph visualization&lt;/li&gt;
&lt;li&gt;render counters&lt;/li&gt;
&lt;li&gt;hotspot profiling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we transform the reactive system from a black box into an observable runtime.&lt;/p&gt;

&lt;p&gt;This not only improves debugging and optimization,&lt;br&gt;
but also helps developers truly understand how reactivity works internally.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DevTools are not just developer conveniences —&lt;br&gt;
they are part of how a runtime teaches its own architecture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, I’ve shared most of the core knowledge I gained while implementing this series.&lt;/p&gt;

&lt;p&gt;There are still many unexplored trade-offs and architectural differences across reactive libraries, and those differences often determine why certain libraries perform better in specific scenarios.&lt;/p&gt;

&lt;p&gt;In the next article, I’d like to share some personal reflections and lessons learned from writing this entire series.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>devtool</category>
    </item>
    <item>
      <title>I published a new article in my Signals series.
In this article, I explore how cooperative scheduling, shouldYield(), and task chunking help avoid blocking the JavaScript main thread.</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Tue, 26 May 2026 03:17:32 +0000</pubDate>
      <link>https://dev.to/luciano0322/i-published-a-new-article-in-my-signals-series-in-this-article-i-explore-how-cooperative-587k</link>
      <guid>https://dev.to/luciano0322/i-published-a-new-article-in-my-signals-series-in-this-article-i-explore-how-cooperative-587k</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/luciano0322/how-react-style-time-slicing-keeps-uis-responsive-2lp0" class="crayons-story__hidden-navigation-link"&gt;How React-Style Time-Slicing Keeps UIs Responsive&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/luciano0322" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" alt="luciano0322 profile" class="crayons-avatar__image" width="256" height="256"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/luciano0322" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Luciano0322
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Luciano0322
                
              
              &lt;div id="story-author-preview-content-3691965" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/luciano0322" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" class="crayons-avatar__image" alt="" width="256" height="256"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Luciano0322&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/luciano0322/how-react-style-time-slicing-keeps-uis-responsive-2lp0" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/luciano0322/how-react-style-time-slicing-keeps-uis-responsive-2lp0" id="article-link-3691965"&gt;
          How React-Style Time-Slicing Keeps UIs Responsive
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/luciano0322/how-react-style-time-slicing-keeps-uis-responsive-2lp0" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/luciano0322/how-react-style-time-slicing-keeps-uis-responsive-2lp0#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>performance</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How React-Style Time-Slicing Keeps UIs Responsive</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Tue, 26 May 2026 03:11:13 +0000</pubDate>
      <link>https://dev.to/luciano0322/how-react-style-time-slicing-keeps-uis-responsive-2lp0</link>
      <guid>https://dev.to/luciano0322/how-react-style-time-slicing-keeps-uis-responsive-2lp0</guid>
      <description>&lt;h2&gt;
  
  
  Quick Recap
&lt;/h2&gt;

&lt;p&gt;In the previous article, we introduced &lt;strong&gt;priority-based and layered schedulers&lt;/strong&gt;, solving the problem of &lt;strong&gt;“which tasks should run first.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However, real-world applications introduce another challenge:&lt;br&gt;
&lt;strong&gt;long-running tasks can still block the main thread.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To keep applications responsive, a scheduler must also support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time-Slicing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cooperative Scheduling&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  The Problem: Long Tasks Blocking the Main Thread
&lt;/h1&gt;

&lt;p&gt;Imagine this scenario:&lt;/p&gt;

&lt;p&gt;The UI thread is executing a &lt;strong&gt;large rendering task&lt;/strong&gt; — for example, updating 5000 list items at once.&lt;/p&gt;

&lt;p&gt;That operation might take tens of milliseconds, or even exceed 100ms before finishing.&lt;/p&gt;

&lt;p&gt;During that time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mouse movement and keyboard input cannot respond immediately&lt;/li&gt;
&lt;li&gt;The UI may freeze and drop below 60 FPS&lt;/li&gt;
&lt;li&gt;Users experience visible stuttering and lag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Priority alone is not enough.&lt;/p&gt;

&lt;p&gt;Even if a task has the correct priority, once execution begins, it can still monopolize the main thread.&lt;/p&gt;


&lt;h1&gt;
  
  
  Time-Slicing
&lt;/h1&gt;

&lt;p&gt;The core idea behind &lt;strong&gt;Time-Slicing&lt;/strong&gt; is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Split a long task into smaller chunks and periodically yield control back to the main thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A task is divided into smaller chunks&lt;/li&gt;
&lt;li&gt;After each chunk, the scheduler checks whether there is remaining execution time&lt;/li&gt;
&lt;li&gt;If not → pause execution and continue later during the next available frame or idle period&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User input and animations remain responsive&lt;/li&gt;
&lt;li&gt;Background work completes progressively over time&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  Cooperative Scheduling
&lt;/h1&gt;

&lt;p&gt;In operating systems, there are two major scheduling models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Preemptive Scheduling&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cooperative Scheduling&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In JavaScript’s single-threaded environment, true preemption is impossible.&lt;/p&gt;
&lt;h2&gt;
  
  
  So frameworks adopt cooperative scheduling instead:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tasks voluntarily check whether they should yield (&lt;code&gt;shouldYield()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;If interrupted, the remaining work is re-queued for later execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is essentially the strategy used by React Concurrent Mode.&lt;/p&gt;


&lt;h1&gt;
  
  
  Example Implementation
&lt;/h1&gt;

&lt;p&gt;Here is a simplified example of a &lt;strong&gt;Time-Slicing + Cooperative Scheduler&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;deadline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;shouldYield&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;deadline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runWithTimeSlicing&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;timeSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;deadline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;timeSlice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;shouldYield&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Not finished → continue later&lt;/span&gt;
  &lt;span class="nf"&gt;queueMicrotask&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;runWithTimeSlicing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeSlice&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;h2&gt;
  
  
  Key Idea
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Each execution is allowed to run for only &lt;code&gt;timeSlice&lt;/code&gt; milliseconds&lt;/li&gt;
&lt;li&gt;Once the budget is exceeded, control returns to the browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents long-running tasks from blocking the main thread entirely.&lt;/p&gt;




&lt;h1&gt;
  
  
  Combining Time-Slicing with Priorities
&lt;/h1&gt;

&lt;p&gt;On top of a layered priority scheduler, we can integrate Time-Slicing strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Immediate / High Priority&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute immediately without slicing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Normal Priority&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use time-slicing and process incrementally&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Low / Idle Priority&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;requestIdleCallback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run only when the browser is idle&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This allows the system to balance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time interactions&lt;/li&gt;
&lt;li&gt;Heavy background updates&lt;/li&gt;
&lt;li&gt;Overall UI smoothness&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Time-Slicing Scheduler Flow
&lt;/h1&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%2Funzsf4a6t2pg4pg2v252.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%2Funzsf4a6t2pg4pg2v252.png" alt="Time-slicing scheduler" width="800" height="815"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Priority scheduling solves the question of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Which task should run first?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Time-Slicing and Cooperative Scheduling solve another equally important problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do we avoid blocking the UI?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Together, these techniques allow systems such as Signals, React, and Vue to remain responsive even under massive update workloads.&lt;/p&gt;

&lt;p&gt;In the next article, we’ll explore &lt;strong&gt;DevTools and Diagnostics&lt;/strong&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inspecting reactive nodes&lt;/li&gt;
&lt;li&gt;Dependency graph visualization&lt;/li&gt;
&lt;li&gt;Render counters&lt;/li&gt;
&lt;li&gt;Hotspot tracing&lt;/li&gt;
&lt;li&gt;Scheduler debugging tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools help us better understand how signals and schedulers behave in real-world applications.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
    <item>
      <title>In my latest signal article, I explore how priority queues and layered execution can help a scheduler preserve interaction responsiveness while still maximizing performance utilization.</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Thu, 21 May 2026 05:22:37 +0000</pubDate>
      <link>https://dev.to/luciano0322/in-my-latest-signal-kernel-article-i-explore-how-priority-queues-and-layered-execution-can-help-a-51eh</link>
      <guid>https://dev.to/luciano0322/in-my-latest-signal-kernel-article-i-explore-how-priority-queues-and-layered-execution-can-help-a-51eh</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/luciano0322/building-a-smarter-scheduler-priority-queues-and-layered-execution-3g6e" class="crayons-story__hidden-navigation-link"&gt;Building a Smarter Scheduler: Priority Queues and Layered Execution&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/luciano0322" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" alt="luciano0322 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/luciano0322" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Luciano0322
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Luciano0322
                
              
              &lt;div id="story-author-preview-content-3647393" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/luciano0322" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Luciano0322&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/luciano0322/building-a-smarter-scheduler-priority-queues-and-layered-execution-3g6e" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/luciano0322/building-a-smarter-scheduler-priority-queues-and-layered-execution-3g6e" id="article-link-3647393"&gt;
          Building a Smarter Scheduler: Priority Queues and Layered Execution
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/luciano0322/building-a-smarter-scheduler-priority-queues-and-layered-execution-3g6e" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/luciano0322/building-a-smarter-scheduler-priority-queues-and-layered-execution-3g6e#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>algorithms</category>
      <category>architecture</category>
      <category>computerscience</category>
      <category>performance</category>
    </item>
    <item>
      <title>Building a Smarter Scheduler: Priority Queues and Layered Execution</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Thu, 21 May 2026 05:19:07 +0000</pubDate>
      <link>https://dev.to/luciano0322/building-a-smarter-scheduler-priority-queues-and-layered-execution-3g6e</link>
      <guid>https://dev.to/luciano0322/building-a-smarter-scheduler-priority-queues-and-layered-execution-3g6e</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In the previous article, we explored the relationship between the Scheduler and the dependency Graph, and discussed the challenges of memory management and dependency management.&lt;/p&gt;

&lt;p&gt;However, in real-world applications, not all tasks have the same level of urgency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some updates must take effect &lt;strong&gt;immediately&lt;/strong&gt;, such as text input from the user.&lt;/li&gt;
&lt;li&gt;Some updates can be &lt;strong&gt;deferred&lt;/strong&gt;, such as animations or low-priority UI updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where &lt;strong&gt;Priority&lt;/strong&gt; and &lt;strong&gt;Layered Scheduling&lt;/strong&gt; become important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Need Priority?
&lt;/h2&gt;

&lt;p&gt;Imagine this scenario: a user is typing into an input field through &lt;code&gt;input.onChange&lt;/code&gt;, while a large background computation is also running, such as a chart re-render.&lt;/p&gt;

&lt;p&gt;If both tasks are placed into the same queue without any priority control, the user input may get blocked, leading to a poor user experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The solution: allow &lt;strong&gt;high-priority tasks&lt;/strong&gt;, such as input events, to be processed first, while lower-priority tasks are deferred.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Common Priority Levels
&lt;/h2&gt;

&lt;p&gt;In modern frontend frameworks, scheduling systems usually define priority levels similar to the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Immediate / Sync&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Examples: text updates in an input field, urgent error messages&lt;/li&gt;
&lt;li&gt;These must respond immediately and should not be delayed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;High Priority&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Main UI interactions, such as state updates triggered by button clicks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Normal Priority&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most non-critical UI updates.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Low Priority / Idle&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background computation, performance statistics, prefetching, and other non-urgent work.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This ensures that the most important interactions for user experience are always handled first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layered Scheduling
&lt;/h2&gt;

&lt;p&gt;Besides priority, another important concept is &lt;strong&gt;layering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We can divide tasks into different &lt;strong&gt;layers&lt;/strong&gt;, where each layer has its own queue, and the Scheduler coordinates the execution between them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Computation Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signal / Computed updates&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;UI Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DOM updates, Virtual DOM diffing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;I/O Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch requests, network operations, storage access&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Idle Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Non-essential background tasks, such as log collection&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This design is somewhat similar to an operating system’s CPU scheduler: different types of tasks are managed separately to prevent them from interfering with each other.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Example Architecture
&lt;/h2&gt;

&lt;p&gt;Here is a simplified version of a Scheduler that supports both priority and layering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;immediate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;low&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Job&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Priority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;immediate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;high&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;low&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scheduleJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;requestFlush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;requestFlush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;queueMicrotask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flushJobs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;flushJobs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Execute jobs by priority&lt;/span&gt;
  &lt;span class="nf"&gt;runQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;immediate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;runQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;runQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;runQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&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;blockquote&gt;
&lt;p&gt;This is a “single-level priority” example.&lt;br&gt;
If we want to introduce real layering, we can further split queues based on &lt;code&gt;Job.layer&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Understanding the Execution Flow Through a Diagram
&lt;/h2&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%2Fithelp.ithome.com.tw%2Fupload%2Fimages%2F20250829%2F20129020Ii0HOwZBdo.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%2Fithelp.ithome.com.tw%2Fupload%2Fimages%2F20250829%2F20129020Ii0HOwZBdo.png" alt="Priority and Layered Scheduler" width="800" height="897"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance and Optimization Strategies
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Batching&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tasks with the same priority can be merged to avoid duplicated computation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Time-Slicing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long-running tasks can be split into smaller chunks to avoid blocking the main thread.&lt;/li&gt;
&lt;li&gt;React Concurrent Mode uses this kind of strategy.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Waterfall Execution&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run high-priority tasks first, then process lower-priority tasks if there is still enough time.&lt;/li&gt;
&lt;li&gt;If time is insufficient, low-priority tasks can be deferred to the next tick.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;Moving from a single queue to a Scheduler with &lt;strong&gt;priority and layering&lt;/strong&gt; has one major goal:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Preserve interaction responsiveness while maximizing performance utilization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why React introduced Concurrent Features, Vue uses a job queue, and signal-based systems are also starting to design more refined scheduling mechanisms.&lt;/p&gt;

&lt;p&gt;In the next article, we will dive deeper into &lt;strong&gt;Time-Slicing and Cooperative Scheduling&lt;/strong&gt;, and explore how a scheduler can keep interactions smooth even when handling expensive tasks.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I published a new article in my Signals series.
This one is about the hidden cost of fine-grained reactivity:
dangling nodes, stale edges, disposal, auto-unlinking, equality strategies, and why the dependency graph is just as important as the Scheduler.</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 18 May 2026 08:01:26 +0000</pubDate>
      <link>https://dev.to/luciano0322/i-published-a-new-article-in-my-signals-series-this-one-is-about-the-hidden-cost-of-fine-grained-3mde</link>
      <guid>https://dev.to/luciano0322/i-published-a-new-article-in-my-signals-series-this-one-is-about-the-hidden-cost-of-fine-grained-3mde</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/luciano0322/signal-internals-managing-memory-and-the-dependency-graph-4hi1" class="crayons-story__hidden-navigation-link"&gt;Signal Internals: Managing Memory and the Dependency Graph&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/luciano0322" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" alt="luciano0322 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/luciano0322" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Luciano0322
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Luciano0322
                
              
              &lt;div id="story-author-preview-content-3611987" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/luciano0322" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Luciano0322&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/luciano0322/signal-internals-managing-memory-and-the-dependency-graph-4hi1" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/luciano0322/signal-internals-managing-memory-and-the-dependency-graph-4hi1" id="article-link-3611987"&gt;
          Signal Internals: Managing Memory and the Dependency Graph
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/luciano0322/signal-internals-managing-memory-and-the-dependency-graph-4hi1" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/luciano0322/signal-internals-managing-memory-and-the-dependency-graph-4hi1#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>architecture</category>
      <category>javascript</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Signal Internals: Managing Memory and the Dependency Graph</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 18 May 2026 07:58:45 +0000</pubDate>
      <link>https://dev.to/luciano0322/signal-internals-managing-memory-and-the-dependency-graph-4hi1</link>
      <guid>https://dev.to/luciano0322/signal-internals-managing-memory-and-the-dependency-graph-4hi1</guid>
      <description>&lt;h2&gt;
  
  
  Quick Recap
&lt;/h2&gt;

&lt;p&gt;In the previous article, we looked at the role of the Scheduler.&lt;/p&gt;

&lt;p&gt;The Scheduler is responsible for arranging downstream jobs after data changes and batching those updates so the system does not recompute everything immediately and repeatedly.&lt;/p&gt;

&lt;p&gt;However, scheduling alone is not enough to make a reactive system stable.&lt;/p&gt;

&lt;p&gt;If a signal system is expected to run for a long time, especially inside real applications where components mount, unmount, reconnect, and re-track dependencies, two topics become unavoidable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;memory management&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dependency graph management&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article focuses on these two layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Do We Need a Graph?
&lt;/h2&gt;

&lt;p&gt;In a fine-grained reactivity system, the relationships between &lt;code&gt;signal&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, and &lt;code&gt;effect&lt;/code&gt; are essentially a &lt;strong&gt;directed graph&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nodes
&lt;/h3&gt;

&lt;p&gt;A node represents a reactive unit in the system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Signal&lt;/strong&gt;: the source of state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computed&lt;/strong&gt;: a derived value calculated from other nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effect&lt;/strong&gt;: a side-effect node, such as DOM updates, logging, or external synchronization&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Edges
&lt;/h3&gt;

&lt;p&gt;An edge represents a dependency relationship.&lt;/p&gt;

&lt;p&gt;When an &lt;code&gt;effect&lt;/code&gt; reads a &lt;code&gt;signal&lt;/code&gt;, or when a &lt;code&gt;computed&lt;/code&gt; reads another &lt;code&gt;computed&lt;/code&gt;, the system creates an edge between the dependent node and the dependency it read.&lt;/p&gt;

&lt;p&gt;This gives the runtime a structured way to answer one important question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When this value changes, who needs to know?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This design serves two major purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Precise Updates
&lt;/h3&gt;

&lt;p&gt;When a signal changes, the runtime does not need to notify the entire world.&lt;/p&gt;

&lt;p&gt;It only follows the graph edges and finds the nodes that are actually affected.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Avoiding Redundant Computation
&lt;/h3&gt;

&lt;p&gt;The graph allows the system to track which computations are still valid and which ones have become stale.&lt;/p&gt;

&lt;p&gt;Instead of recomputing everything eagerly, the runtime can mark affected nodes as dirty and only recompute them when necessary.&lt;/p&gt;

&lt;p&gt;Without a graph, the Scheduler would be forced to use a broad broadcast mechanism. Every update would become much more expensive because the system would lose the ability to know what is actually related to what.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Memory Management Problem
&lt;/h2&gt;

&lt;p&gt;Once we introduce a graph, we also introduce ownership and lifetime problems.&lt;/p&gt;

&lt;p&gt;A graph is not just a set of values. It is a set of references.&lt;/p&gt;

&lt;p&gt;If those references are not cleaned up correctly, the system may continue holding onto nodes that should already be gone.&lt;/p&gt;

&lt;p&gt;This is where memory management becomes part of the core design.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Dangling Nodes
&lt;/h2&gt;

&lt;p&gt;A dangling node appears when an &lt;code&gt;effect&lt;/code&gt; or &lt;code&gt;computed&lt;/code&gt; is no longer needed, but its node is still retained by the graph.&lt;/p&gt;

&lt;p&gt;This commonly happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a React component unmounts&lt;/li&gt;
&lt;li&gt;a Vue component is destroyed&lt;/li&gt;
&lt;li&gt;a manually created effect is no longer used&lt;/li&gt;
&lt;li&gt;a derived computation becomes unreachable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the corresponding graph node is not cleaned up, it may continue to be referenced by upstream nodes.&lt;/p&gt;

&lt;p&gt;That means the runtime may still keep it alive, even though the application no longer needs it.&lt;/p&gt;

&lt;p&gt;This is a typical source of memory leaks in reactive systems.&lt;/p&gt;

&lt;p&gt;The solution is straightforward in principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a node is disposed, unlink all of its upstream dependencies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Disposal is not only about stopping future execution. It is also about removing the node from the graph so it can be released.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Stale Edges
&lt;/h2&gt;

&lt;p&gt;A stale edge appears when a node's dependency relationship changes, but the old edge remains in the graph.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sourceA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sourceB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;When &lt;code&gt;enabled&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, this computed value depends on &lt;code&gt;sourceA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;enabled&lt;/code&gt; becomes &lt;code&gt;false&lt;/code&gt;, it should stop depending on &lt;code&gt;sourceA&lt;/code&gt; and start depending on &lt;code&gt;sourceB&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;If the old edge to &lt;code&gt;sourceA&lt;/code&gt; is not removed, then future updates to &lt;code&gt;sourceA&lt;/code&gt; may still mark this computed value as stale, even though it no longer reads from &lt;code&gt;sourceA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is not only inefficient. It also means the graph no longer reflects the real dependency structure of the program.&lt;/p&gt;

&lt;p&gt;The solution is to rebuild dependency edges during the tracking phase.&lt;/p&gt;

&lt;p&gt;In practice, this usually means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;collect dependencies while executing the tracked function&lt;/li&gt;
&lt;li&gt;compare the new dependencies with the old ones&lt;/li&gt;
&lt;li&gt;remove edges that are no longer used&lt;/li&gt;
&lt;li&gt;add edges that are newly discovered&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is why a signal system needs explicit &lt;code&gt;link&lt;/code&gt; and &lt;code&gt;unlink&lt;/code&gt; operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. GC and Retain Cycles
&lt;/h2&gt;

&lt;p&gt;JavaScript's garbage collector can handle many circular references.&lt;/p&gt;

&lt;p&gt;However, that does not mean a reactive runtime can ignore reference ownership.&lt;/p&gt;

&lt;p&gt;If the internal graph structure keeps strong references to nodes that are no longer reachable from user code, those nodes may still remain alive.&lt;/p&gt;

&lt;p&gt;The problem is not simply that a cycle exists.&lt;/p&gt;

&lt;p&gt;The real problem is that the runtime may accidentally become the owner of objects that should have been released.&lt;/p&gt;

&lt;p&gt;There are several ways to reduce this risk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avoid unnecessary bidirectional ownership&lt;/li&gt;
&lt;li&gt;make disposal explicit where lifecycle boundaries are clear&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;WeakMap&lt;/code&gt; for metadata lookup when the runtime should not own the object&lt;/li&gt;
&lt;li&gt;consider &lt;code&gt;WeakRef&lt;/code&gt; only when the use case truly requires weak object references&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my own design preference, I would start with &lt;strong&gt;explicit disposal&lt;/strong&gt; first.&lt;/p&gt;

&lt;p&gt;It makes lifecycle boundaries easier to reason about. Weak references can be useful, but they should not become a replacement for a clear ownership model.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Minimal Graph Structure
&lt;/h2&gt;

&lt;p&gt;In a signal system, there is usually a dedicated &lt;strong&gt;Graph Layer&lt;/strong&gt; responsible for managing nodes, dependencies, subscribers, and disposal.&lt;/p&gt;

&lt;p&gt;A simplified node structure may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// upstream dependencies&lt;/span&gt;
  &lt;span class="nl"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// downstream subscribers&lt;/span&gt;
  &lt;span class="nl"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// whether the node is dirty&lt;/span&gt;
  &lt;span class="nl"&gt;disposed&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// whether the node has been released&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different libraries may use different names or organize the structure differently, but the underlying idea is usually similar.&lt;/p&gt;

&lt;p&gt;If you read through the source code of several reactive libraries, you will often find the same core concepts under different abstractions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Linking and Unlinking
&lt;/h2&gt;

&lt;p&gt;When a reactive node reads another node during tracking, the runtime creates an edge.&lt;/p&gt;

&lt;p&gt;A minimal implementation may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&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;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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;Here, &lt;code&gt;source&lt;/code&gt; is the dependent node, and &lt;code&gt;target&lt;/code&gt; is the node it depends on.&lt;/p&gt;

&lt;p&gt;So the relationship is stored in two directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;source.deps&lt;/code&gt; points to upstream dependencies&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;target.subs&lt;/code&gt; points to downstream subscribers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This bidirectional structure makes propagation efficient, but it also means cleanup must be handled carefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Disposing a Node
&lt;/h2&gt;

&lt;p&gt;When an effect is destroyed, the runtime must remove it from its upstream dependencies.&lt;/p&gt;

&lt;p&gt;A simplified disposal function may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&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;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disposed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;This ensures that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stale dependencies do not remain in the graph&lt;/li&gt;
&lt;li&gt;upstream nodes no longer retain the disposed node&lt;/li&gt;
&lt;li&gt;memory can be released after a component or effect is destroyed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, &lt;code&gt;dispose()&lt;/code&gt; is not just an API for stopping an effect.&lt;/p&gt;

&lt;p&gt;It is a graph cleanup operation.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Scheduler Uses the Graph
&lt;/h2&gt;

&lt;p&gt;The Scheduler does not work in isolation.&lt;/p&gt;

&lt;p&gt;It relies on the graph to know which nodes should be updated after a change.&lt;/p&gt;

&lt;p&gt;A typical flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;signal.set()&lt;/code&gt; updates the value and marks the signal node as stale.&lt;/li&gt;
&lt;li&gt;The Scheduler queues affected jobs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flushJobs()&lt;/code&gt; drains the queue.&lt;/li&gt;
&lt;li&gt;The runtime follows &lt;code&gt;subs&lt;/code&gt; to propagate invalidation downstream.&lt;/li&gt;
&lt;li&gt;If some nodes no longer have subscribers, the runtime may auto-unlink or dispose them.&lt;/li&gt;
&lt;/ol&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%2Fb2zf94g84v3zbcfbrwg0.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%2Fb2zf94g84v3zbcfbrwg0.png" alt="Scheduler and graph interaction" width="800" height="1342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The important point is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Scheduler decides when work should run, but the graph decides where the work should propagate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without the graph, scheduling becomes blind.&lt;/p&gt;

&lt;p&gt;Without the Scheduler, graph updates can become chaotic and repetitive.&lt;/p&gt;

&lt;p&gt;A stable signal runtime needs both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Optimization Strategies
&lt;/h2&gt;

&lt;p&gt;In small examples, basic graph management may be enough.&lt;/p&gt;

&lt;p&gt;But in real applications, the graph may grow, shrink, and change shape constantly.&lt;/p&gt;

&lt;p&gt;That is why signal systems usually need additional optimization strategies.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Auto-Unlink
&lt;/h2&gt;

&lt;p&gt;In the standard &lt;code&gt;link&lt;/code&gt; / &lt;code&gt;unlink&lt;/code&gt; flow, a node may eventually lose all of its downstream subscribers.&lt;/p&gt;

&lt;p&gt;If that node still keeps its upstream dependencies, invalid edges may remain in the graph for a long time.&lt;/p&gt;

&lt;p&gt;This may not immediately cause incorrect behavior, but it can make the graph larger than necessary.&lt;/p&gt;

&lt;p&gt;The solution is &lt;strong&gt;auto-unlink&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;autoUnlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;clear&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;With this approach, when a &lt;code&gt;computed&lt;/code&gt; or &lt;code&gt;effect&lt;/code&gt; no longer has subscribers, it can clean up its upstream dependencies automatically.&lt;/p&gt;

&lt;p&gt;This prevents dangling dependency relationships from staying in the graph.&lt;/p&gt;

&lt;p&gt;In the context of React or Vue, this is similar to removing event listeners during unmount.&lt;/p&gt;

&lt;p&gt;The goal is the same: release resources when the lifecycle has ended.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Equals Strategy and Performance
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;signal.set()&lt;/code&gt; flow, we usually perform an equality check before triggering invalidation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;markStale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thisNode&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;This avoids unnecessary updates.&lt;/p&gt;

&lt;p&gt;However, equality checking is not free. Different equality strategies have different trade-offs.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;Object.is&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Object.is&lt;/code&gt; is usually the default option.&lt;/p&gt;

&lt;p&gt;It is native to JavaScript, fast, and predictable.&lt;/p&gt;

&lt;p&gt;It works especially well for primitives and reference equality.&lt;/p&gt;

&lt;p&gt;The limitation is that objects and arrays are compared by reference. If a new object is created, it will be treated as a new value even if its contents are structurally similar.&lt;/p&gt;




&lt;h3&gt;
  
  
  Shallow Equal
&lt;/h3&gt;

&lt;p&gt;Shallow equality can be useful for common object-shaped UI state.&lt;/p&gt;

&lt;p&gt;It can reduce unnecessary updates when object references change but the top-level fields remain the same.&lt;/p&gt;

&lt;p&gt;The trade-off is that shallow comparison has an &lt;code&gt;O(n)&lt;/code&gt; cost based on the number of keys or items being compared.&lt;/p&gt;




&lt;h3&gt;
  
  
  Custom Equal
&lt;/h3&gt;

&lt;p&gt;Custom equality gives users full control.&lt;/p&gt;

&lt;p&gt;It can support domain-specific comparison rules, Immutable.js structures, AST nodes, or deep equality.&lt;/p&gt;

&lt;p&gt;However, this flexibility also introduces risk.&lt;/p&gt;

&lt;p&gt;A poorly designed custom equality function can become more expensive than the update it tries to avoid.&lt;/p&gt;




&lt;h3&gt;
  
  
  Practical Guidance
&lt;/h3&gt;

&lt;p&gt;For high-frequency updates, such as game loops, pointer tracking, animation, or real-time telemetry, &lt;code&gt;Object.is&lt;/code&gt; is usually the safest default.&lt;/p&gt;

&lt;p&gt;For form state or regular UI state, shallow equality can be a pragmatic compromise.&lt;/p&gt;

&lt;p&gt;For complex domain data, custom equality may be useful, but it should be introduced deliberately.&lt;/p&gt;

&lt;p&gt;The key is not to make equality smarter by default.&lt;/p&gt;

&lt;p&gt;The key is to choose the cheapest strategy that preserves correctness for the use case.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Lazy Disposal
&lt;/h2&gt;

&lt;p&gt;Some systems delay the actual cleanup work instead of disposing nodes immediately.&lt;/p&gt;

&lt;p&gt;This is sometimes called lazy disposal.&lt;/p&gt;

&lt;p&gt;The benefit is that it can reduce immediate overhead during hot update paths.&lt;/p&gt;

&lt;p&gt;The cost is that the runtime now needs another mechanism to decide when deferred cleanup should happen.&lt;/p&gt;

&lt;p&gt;Lazy disposal is not automatically better than explicit disposal.&lt;/p&gt;

&lt;p&gt;It is a trade-off between immediate cleanup cost and delayed resource retention.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Weak References for Dependencies
&lt;/h2&gt;

&lt;p&gt;Some metadata can be stored through &lt;code&gt;WeakMap&lt;/code&gt; so the runtime does not accidentally extend the lifetime of user-owned objects.&lt;/p&gt;

&lt;p&gt;This is useful when the runtime needs to associate internal metadata with external objects but should not become their owner.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WeakRef&lt;/code&gt; can also be used in more advanced cases, but it should be applied carefully.&lt;/p&gt;

&lt;p&gt;Weak references can help reduce retention problems, but they also make lifecycle behavior harder to reason about.&lt;/p&gt;

&lt;p&gt;For most signal systems, explicit graph cleanup should still be the foundation.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Layered Scheduler
&lt;/h2&gt;

&lt;p&gt;Another optimization is to split work into different layers.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;computation layer&lt;/li&gt;
&lt;li&gt;UI update layer&lt;/li&gt;
&lt;li&gt;I/O layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not all jobs have the same urgency.&lt;/p&gt;

&lt;p&gt;A derived value recomputation, a DOM update, a network request, and a logging effect should not necessarily be scheduled with the same priority.&lt;/p&gt;

&lt;p&gt;Once the runtime can distinguish these layers, the Scheduler can make more precise decisions.&lt;/p&gt;

&lt;p&gt;This becomes especially important when the system needs to support larger applications, async resources, streaming updates, or framework adapters.&lt;/p&gt;




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

&lt;p&gt;The Scheduler is not just a mechanism for running jobs later.&lt;/p&gt;

&lt;p&gt;A serious signal system also needs to maintain its dependency graph and manage memory correctly.&lt;/p&gt;

&lt;p&gt;The graph tells the runtime where changes should propagate.&lt;/p&gt;

&lt;p&gt;Memory management ensures that obsolete nodes and edges do not stay alive forever.&lt;/p&gt;

&lt;p&gt;Together, these two layers determine whether the system can remain efficient after running for a long time.&lt;/p&gt;

&lt;p&gt;This is why implementations like Solid, Vue, and Preact Signals all contain dedicated logic for dependency tracking, graph cleanup, and lifecycle management.&lt;/p&gt;

&lt;p&gt;In the next article, we will continue with &lt;strong&gt;priority and layered scheduling&lt;/strong&gt;: how to optimize different kinds of tasks by giving the Scheduler a more structured execution model.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
    <item>
      <title>A Scheduler is the hidden commander of a reactivity system.
It decides not whether something should run, but when it should run.

New article: Building a Signal Scheduler</title>
      <dc:creator>Luciano0322</dc:creator>
      <pubDate>Mon, 11 May 2026 00:36:15 +0000</pubDate>
      <link>https://dev.to/luciano0322/a-scheduler-is-the-hidden-commander-of-a-reactivity-system-it-decides-not-whether-something-5c3a</link>
      <guid>https://dev.to/luciano0322/a-scheduler-is-the-hidden-commander-of-a-reactivity-system-it-decides-not-whether-something-5c3a</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/luciano0322/building-a-signal-scheduler-sync-batch-priority-and-lazy-execution-2okf" class="crayons-story__hidden-navigation-link"&gt;Building a Signal Scheduler: Sync, Batch, Priority, and Lazy Execution&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/luciano0322" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" alt="luciano0322 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/luciano0322" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Luciano0322
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Luciano0322
                
              
              &lt;div id="story-author-preview-content-3543861" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/luciano0322" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F964885%2F18f31678-b62e-4519-8ee6-21f434e4b29c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Luciano0322&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/luciano0322/building-a-signal-scheduler-sync-batch-priority-and-lazy-execution-2okf" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 11&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/luciano0322/building-a-signal-scheduler-sync-batch-priority-and-lazy-execution-2okf" id="article-link-3543861"&gt;
          Building a Signal Scheduler: Sync, Batch, Priority, and Lazy Execution
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/luciano0322/building-a-signal-scheduler-sync-batch-priority-and-lazy-execution-2okf" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/luciano0322/building-a-signal-scheduler-sync-batch-priority-and-lazy-execution-2okf#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>architecture</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
