惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
S
Security @ Cisco Blogs
S
Secure Thoughts
Attack and Defense Labs
Attack and Defense Labs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
Recent Commits to openclaw:main
Recent Commits to openclaw:main
H
Hacker News: Front Page
博客园 - 叶小钗
H
Heimdal Security Blog
Microsoft Security Blog
Microsoft Security Blog
Forbes - Security
Forbes - Security
AI
AI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Troy Hunt's Blog
罗磊的独立博客
Application and Cybersecurity Blog
Application and Cybersecurity Blog
爱范儿
爱范儿
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
TaoSecurity Blog
TaoSecurity Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
D
DataBreaches.Net
Recent Announcements
Recent Announcements
Schneier on Security
Schneier on Security
C
Cisco Blogs
美团技术团队
D
Docker
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
月光博客
月光博客
雷峰网
雷峰网
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
H
Hackread – Cybersecurity News, Data Breaches, AI and More
A
Arctic Wolf
B
Blog RSS Feed
Cisco Talos Blog
Cisco Talos Blog
C
Cybersecurity and Infrastructure Security Agency CISA
V
Vulnerabilities – Threatpost
V2EX - 技术
V2EX - 技术
Y
Y Combinator Blog
N
News and Events Feed by Topic
www.infosecurity-magazine.com
www.infosecurity-magazine.com
W
WeLiveSecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
G
GRAHAM CLULEY
Jina AI
Jina AI
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Hacker News
The Hacker News

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Why I think Component-Driven Development needs a rethink in the Signal era
Alex · 2026-05-18 · via DEV Community

Component-Driven Development assumed a render model that signal-based Angular has quietly left behind. The tooling has not caught up, and I am not sure simply patching it will be enough.

The moment that made me write this

I was building ng-prism, an Angular-native component showcase tool I maintain, and I needed to update a component's inputs from a controls panel. The naive version looked roughly like this:

const ref = vcr.createComponent(ButtonComponent);
const instance = ref.instance;

instance.variant = 'primary';
instance.label  = 'Save';
ref.changeDetectorRef.detectChanges();

Enter fullscreen mode Exit fullscreen mode

It works on a pre-signal component. On a signal-based component it quietly breaks the component. variant and label are no longer plain properties. They are InputSignal objects, callable as variant(). Angular stores the InputSignal as a plain class field; there is no defineProperty setter or proxy to intercept the write. So the assignment just overwrites the field reference with a string, and Angular never finds out. The next render then blows up the first time the template evaluates variant(), because variant is no longer a function. I verified this against @angular/core 21.2.0: createInputSignal() returns a plain function with a [SIGNAL] symbol attached, no setter trap in sight.

The fix is one line:

ref.setInput('variant', 'primary');
ref.setInput('label',   'Save');

Enter fullscreen mode Exit fullscreen mode

That is the version that lives in ng-prism today, in the renderer effect that drives every showcase:

// packages/ng-prism/src/app/renderer/prism-renderer.component.ts
// Simplified; real version also handles content projection and unknown-input warnings.
effect(() => {
  const inputs = this.rendererService.inputValues();
  const ref    = this.componentRef;
  if (!ref) return;

  performance.mark('prism:rerender:start');
  for (const [key, value] of Object.entries(inputs)) {
    ref.setInput(key, value);
  }
  // setInput() already marks dirty and schedules CD. The explicit
  // detectChanges() here forces it to run synchronously so the
  // performance.mark below wraps the actual render, not just the
  // dirty-marking. Also keeps timings predictable under zoneless.
  ref.changeDetectorRef.detectChanges();
  performance.mark('prism:rerender:end');
});

Enter fullscreen mode Exit fullscreen mode

This is a tiny detail, but it is the symptom of a much bigger thing. The way Component-Driven Development (CDD) thinks about a component (props in, render out, args table on the side) was modelled on a world where component inputs were plain properties. That world is gone in Angular. And once you stop assuming it, a lot of the tooling we have built over the last decade starts to look like it is solving the wrong problem.

The implicit model behind classic CDD

Look at what almost every CDD tool, Storybook included, has agreed on for years:

  1. A component is a pure-ish function of its inputs.
  2. Inputs are configured as a flat dictionary, the “args”.
  3. A “story” is a specific value of that dictionary.
  4. Changing args triggers a discrete re-render cycle.
  5. Addons (a11y, viewport, knobs, controls) hook into that cycle.

In the pre-Hooks era this was the right abstraction. React class components had setState. Angular had @Input() decorators and ngOnChanges. Vue had options API. All of them had a clear, discrete moment where a property got assigned, the framework noticed, and the component re-rendered top-down. Args tables map onto that perfectly. Whatever knob you turn becomes a property assignment, which becomes an ngOnChanges call, which becomes a render.

This is also why Storybook’s args model felt so natural for so long. Args are properties. Properties are state. State drives render. Story = scenario = property snapshot.

It was a beautiful, simple model. It is also, for Angular today, the wrong abstraction.

What Signals actually changed

I do not want to retread the “signals are great” territory. What matters for CDD is more specific.

Inputs are no longer properties. An InputSignal is a callable getter. From outside the component, the only correct way to push a value into it is ComponentRef.setInput(name, value). There is no property to assign anymore. Tools that still build their abstractions on “assign this prop” are not even wrong yet, they just produce broken components.

Components are nodes in a reactive graph, not pure functions of inputs. Half of a real-world signal-based component is computed() derived state. Those derived signals are part of the component’s behaviour. They are not inputs, but they are also not opaque internal state. An args table cannot represent them. A story shapes inputs, not derived state.

Lifecycle work is increasingly split between explicit hooks and reactive subscriptions. ngOnInit and ngOnDestroy still exist and are still idiomatic for a lot of cases (subscriptions, teardown, one-shot setup). What has changed is that ngOnChanges is largely irrelevant for signal inputs, and a growing share of the work that used to live in lifecycle hooks now lives inside effect() or computed() that is read by the template. The places where work happens have multiplied, and a chunk of it fires on a much finer-grained schedule than mount → change → destroy. CDD tools that visualise lifecycle as those three states are visualising a real but shrinking slice of what the component actually does.

Re-renders are continuous, not cycle-based. Zoneless Angular still has ApplicationRef.tick() and a ChangeDetectionScheduler. What is gone is the implicit tick driven by zone.js intercepting every async operation. In a zoneless app, change detection runs because the scheduler decided to run it, which in practice is because a signal flagged a view dirty. From the outside that looks less like a single render cycle and more like a graph of signals notifying their dependents at fine granularity. An addon that says “re-run my check on each render cycle” has no clean event to listen to, because there is no single render cycle visible at the public surface of the component.

The pre-signal mental model is not catastrophically wrong. It is just lossy. And every layer of CDD tooling pays the price of that loss somewhere.

Where the tooling lags, concretely

Three examples I have hit while building ng-prism.

1. Controls and args still treat inputs as a flat property bag.

The Storybook args object and ng-prism’s own variant config both look like this:

{ variant: 'primary', label: 'Save', disabled: false }

Enter fullscreen mode Exit fullscreen mode

That is fine as a starting state. It is wrong as a model of how the component actually consumes its inputs. A signal-based input has a default expression, can be required, can be tied through computed() into other state, and can be read multiple times per render. None of that is in the args object. The args object is a snapshot of a tree it cannot see.

2. A11y, perf, and visual-diff addons assume “render happened, now check”.

axe-core, layout measurements, screenshot capture: they all rely on a moment where the DOM has settled and you can inspect it. Angular does give you some primitives here. afterNextRender() fires once after the next render. afterRender() fires after every render. ApplicationRef.isStable exposes a stable-state observable. None of those quite match what an a11y or visual-diff addon actually wants, which is closer to “the reactive graph has been quiet for N milliseconds across multiple microtasks, and I am now safe to walk the DOM”. That concept does not exist as a framework primitive. So you debounce, you wait for animation frames, you hope. In ng-prism the built-in a11y audit runs after a 500ms debounce on signal changes (A11yAuditService.scheduleAudit, default debounceMs = 500), which works in practice but feels like a workaround. I am not sure a true “graph is quiescent” signal even makes sense in a system designed to update continuously.

3. The “scenario” unit is too coarse.

A Storybook story is one set of args. A ng-prism variant is one set of input values. Neither can express “this component embedded in a parent that emits a signal stream over time”. The interesting bug in a signal-based component is rarely the static case. It is the transition. It is the moment when an upstream computed() updates twice in the same microtask and your effect runs once instead of twice. You cannot represent that with { variant: 'primary' }.

What I tried in ng-prism, honestly

ng-prism started life with the same implicit model as Storybook. Components have inputs. Inputs have values. Variants are named tuples of input values. The decorator looks similar:

@Showcase({
  title: 'Button',
  variants: [
    { name: 'Primary', inputs: { variant: 'primary', label: 'Save' } },
    { name: 'Danger',  inputs: { variant: 'danger', disabled: true } },
  ],
})

Enter fullscreen mode Exit fullscreen mode

What ng-prism does that I think is on the right track:

  • Uses setInput() for every input push, never property assignment. So at least the signal contract is respected. This is the absolute floor and a surprising number of Angular-adjacent tools still get it wrong.
  • Drives the rendering loop with effect() on a signal of input values, not with a render-cycle event. The renderer reacts to whatever upstream signal happens to change.
  • Treats the scanner as a build-time concern. Signal inputs are recognised via the TypeScript Compiler API at build time, so the runtime never has to read decorator metadata. The decorator itself is literally a no-op, just a marker.
  • Supports zoneless via an opt-in flag in the ng add schematic (--zoneless), which wires up provideZonelessChangeDetection() and drops zone.js from polyfills. The default still ships with zone.js, but nothing in the renderer depends on an implicit zone tick; change detection runs because something signal-shaped told it to.

What ng-prism does not solve yet, and where I think the whole category is still stuck:

  • The variant model is still a flat input snapshot. I cannot describe a component as “embedded in a parent that pushes this signal stream over 2 seconds”.
  • computed() derived state is invisible in the UI. You see the inputs you set. You do not see the graph that hangs off them. For some components, that graph is the interesting part.
  • The a11y, perf, and box-model panels all hook into renderer output the way an old addon would. They debounce. They do not subscribe to the actual reactive graph the component is part of.
  • The “code snippet” feature generates a template string from input values. It cannot show how the component would behave in a parent where one of those inputs is itself a signal.

I am being deliberate about this in the docs. I do not want ng-prism to claim a level of insight into signal-based components that it does not yet deliver.

What I think the next generation should look like

This is the speculative part, so take it as opinion.

I think the unit of CDD will stop being “the component with these props” and start being “the component embedded in a reactive context”. A scenario will look more like a tiny fixture: this component, mounted under this parent, with these signal sources feeding it, over time. Closer to a Playwright scenario than a Storybook story.

I think tooling will need to render the reactive graph, not just the visual output. For a serious component, the dependency graph between inputs, computed(), and effect() is the documentation. We render the box and the controls. We should also be able to render the graph.

I think the addon model needs to flip. Instead of hooking into a render lifecycle that no longer exists, addons should subscribe to specific signals. Visual diff subscribes to the renderedElement signal. A11y subscribes to the same. Perf subscribes to a “graph quiescent” signal that the framework would have to expose. None of this lives in Storybook’s current architecture, and bolting it on is not obviously the right answer.

And I think “args” as a UI metaphor has run its course. A signal-based component is not configured by setting properties. It is wired up. The control surface should reflect that.

Closing

I am not arguing that Storybook is bad, or that the people maintaining it have missed something obvious. They built the right tool for the framework world of 2018, and that tool became a standard. Standards lag by definition. That is fine.

What I am arguing is that the abstraction is starting to leak in ways that matter. Signal-based Angular is a different kind of component runtime, not a faster version of the old one, and the surface area that CDD tooling was designed against has changed underneath it.

I am building ng-prism partly to test that hypothesis in code, not just in prose. I am also fully prepared to find out that I am wrong, that an args table plus setInput() plus a debounce is good enough for 95% of cases, and that the rest is academic. Possible. But I do not think it is, and I would rather find out by building than by predicting.

If you have hit the same kind of friction, or you have a counter-example where the args model still maps cleanly onto a signal-heavy component, I would genuinely like to hear it. That is more useful than another “signals are great” thread.


ng-prism is open source on GitHub. Feedback and counter-examples especially welcome.