JS Party – Episode #310

Angular Signals

with Pavel Kozlowski & Alex Rickabaugh

All Episodes

KBall & Amal interview Alex & Pavel from the Angular Signals team. They cover the history, how the Angular team decided to move to signals, what the new mental model looks like, migration path & even dive into community integrations and future roadmap.

Featuring

Sponsors

Fly.ioThe home of Changelog.com — Deploy your apps and databases close to your users. In minutes you can run your Ruby, Go, Node, Deno, Python, or Elixir app (and databases!) all over the world. No ops required. Learn more at fly.io/changelog and check out the speedrun in their docs.

Notes & Links

📝 Edit Notes

Chapters

1 00:00 It's party time, y'all
2 00:56 Hello JS Party people!
3 01:30 Welcoming our guests
4 02:31 Getting to know Alex
5 04:31 Getting to know Pavel
6 06:47 The Angular Signals story
7 14:59 Checking all the boxes
8 20:22 Defining Signals in Angular
9 22:54 Click click on dirty checking
10 27:07 A mental model for Signals
11 37:27 The pros & cons of adoption
12 49:23 Amal's barrier to entry
13 51:27 The Angular community
14 57:32 The future of Signals
15 1:01:53 NGRX <> Signals
16 1:03:56 KBall signing out
17 1:05:38 Next up on the pod

Transcript

📝 Edit Transcript

Changelog

Play the audio to listen along while you enjoy the transcript. 🎧

Hello, JS party people! The sound of those BMC beats means that yes, it is once again your favorite party about JavaScript and the web. I’m Kball, I’m your host today. I am joined by one of my favorite co-hosts, Amal. Amal, how’s it going?

One of my favorites, or favorite? Come on, okay? [laughs]

Today you’re my favorite, Amal. I invited you specially to this episode.

That is true. I feel so special. And it just feels great to not be hosting, you know? I get to relax… No, anyways, I’m honored to be here today, and we have a very exciting topic, so…

We do. And that gets us into introducing our special guests today. We have two experts from the Angular Signals team. First off, let me introduce Alex Rickabaugh. Alex, how’s it going?

It’s going great. Fantastic. How about yourself?

I am so excited to be here, and we have the two of you partially because we’ve heard from our listeners that we don’t cover Angular enough. So endeavouring to address that, both today and in a another episode coming up soon. Our second guest, Pavel Kozlowski.

Hey! Super-excited to be here. And honestly, there are two things - this is outside of the Android community, so we can talk about patterns across frameworks, but also doing this with my partner in crime at Signals , Alex here, is just always fun.

Yeah. Well, we’re excited to get in. But before we do that, why don’t we have the two of you introduce yourselves a little bit more? Tell us about your background, and how you got into the Angular world and team. Let’s start with Alex.

Sure. Yeah, so I’m a software engineer at Google. I’ve been there since 2010. It was not my first job out of college, but it was my first real job out of college. And I used to work in the Google sales organization on the CRM tools that our ad sales teams used… And at the time, we were rewriting it from a big Java GWT application to an Angular application; actually, in Angular Dart. And I was working on the backend, data fetching layer of that application. And it just so happened that our director was also the director of the Angular team, Brad Green, back in the day. And so Brad asked me “Hey, do you want to work on this Angular thing full time, and solve the same problems at a much bigger scale?” And I said yes without really having any idea what I was getting myself into. I had never really thought about a framework, or what the framework’s role in the application ecosystem was. It just sounded interesting, so I kind of rolled my stuff over to 100 feet to the dust, the Angular team next door.

That story reminds me of Devil Wears Prada. I don’t know if any of you have seen that movie… Where Anne has the job, runway, working at this – she’s at this fancy magazine company where 1,000 girls would kill for that job, but she’s just “Whatever. It’s just a job.” You know? [laughs] That’s awesome. Literally, 10 million people would kill for that job.

I think I was there one month on the team or something, when they asked “Hey, do you want to go to this Angular conference in Las Vegas?” And I said “That sounds fantastic. I’ve never been to Vegas. Let’s do it.” And I think Brad was “Okay, great. You’re giving the keynote.” “I’m what now?” I’d never done a public talk, never even been to a JavaScript conference before that. It’s “Wow, okay… What did I get myself into?”

How about you, Pavel?

So I think I actually was messing with frameworks for as long as I can remember. For some reason, I was always gravitating toward teams that do those developer tooling. And I guess there’s something magical about really doing tools that shape the way we think about the problem and solutions.

So I was working in a company doing software for the travel industry, that had very strong framework themes (surprise, surprise), and I was on this team. But that was an internal UI JavaScript framework. And at some point we wanted to move to something more open source. But having this strong team and interest in constructing those tools, we wanted to have some more foot on the ground or hands in the actual code. So we did something I consider pretty unique. We started a cooperation between my company and the Android team at Google. And for several years, I was actually working on the framework as the open source contributor, and joined Google about two years ago. So in total, I think I was on a team, on and off, for a decade, and still enjoying it immensely.

Okay, so Pavel, I have a funny story to tell you. It’s not as cool as the Devil Wears Prada story, but I think I met you a decade ago, in the Boston area, where you were at a meetup that my company was hosting, where you were telling us that you had joined the Angular team, and you were the first person who wasn’t a Googler that was part of core. And I was “Man, that’s so cool.” I don’t know if you remember that event, or know what I’m talking about…

I do remember being – I think Jeff Whelpley was running it.

Yes, Jeff Whelpley organized it. And Sharon DiOrio.

[00:06:18.03] So I remember being – I was actually on a business trip then, and I was talking with Jeff, and he was “Hey, we’ve got this meetup, maybe you just want to pass by.” And I do remember that we had a Q&A, and a fireside chat.

We did, and it was awesome.

That’s just amazing.

Yeah, what a small world. I was just “Hold on a second… I know this story.” Oh, my goodness. Yeah, okay. Super-cool. Yeah, anyways. Small world.

This is fun.

That’s awesome. Yeah. Well, so continuing on a little bit, y’all are not just here because you work on Angular, but specifically, the two of you have been working together on Angular Signals for some time. I don’t know how long. I know Signals is a relatively new feature for Angular… Do you want to give us a little bit of the background? Like, why Signals, and how did this all come together?

Now, it will be interesting. I think we want to hear two stories, and then compare notes…

So Angular JS to Angular - that transition was almost 10 years ago now, that we were going through this rethinking of how the framework would work. And 10 years is about the right time for those kinds of introspection moments. So around 2021, post pandemic, the Angular team was able to be together in-person for the first time since switching to remote work. We sat down and held a – I to call it a design review after the fact, where we did a lot of reflection on what state the framework was in, all of the different design decisions that had gone into making Angular what it was over the years… Those foundational pieces, and whether they were still working for us. Some things were no longer relevant, because the web had evolved in that timeframe, the job of the framework had shifted significantly…

Frameworks - their main job used to be making the experience the same across all of the different browsers, especially Internet Explorer. And now, in the 2020 era, frameworks are more – like, their primary focus is on offering the best possible developer experience and velocity, really being a tool that enables developers to code and move much more quickly, and ship features much more quickly.

So we were looking at Angular through this lens, of what still will work for us for the next 10 years, and which pieces might need changing. And one of the things that we identified as needing to invest in is the framework’s reactivity story, which was built around this library called Zone.js… And how Zone interacted with the browser, and monkey-patched all the different browser APIs to give us this notification that something might have happened in the application that the framework needs to handle. And for a variety of different reasons, this pattern we didn’t think was going to be viable for the next 10 years. Some of that is related to browsers and browser technology; Zone physically can’t interact with async/await. So we were seeing new browser features that were incompatible with this model.

At the same time, Angular applications, Angular as a community was scaling, our applications were getting bigger, people were writing larger and larger projects with it, and we were seeing some of the cracks of the Zone.js model, where this approach of “You just code, you just write your state updates, and the framework will figure it out”, that scales to a point, and then it stops working. You start having to actually really care about what’s going on. Or you end up with situations where the performance of the application gets really bad, because you’ve tripped over some edge case without realizing it.

[00:10:03.15] And so we started looking around for what is the foundational piece that we can replace Zone.js with. How can we overhaul the reactivity model of the framework to carry us for the next 10 years, and address the core problems with the Zone.js designs. And after a number of sessions with Pavel, where we sat down and tried out all kinds of different ideas, Signals came to the forefront as checking all of the boxes for what we were really looking for.

Yeah, I think my story is very similar, so at least it’s good that publicly we’ve got the same coherent story. But I do remember this pretty much emotional team get-together, where – you know, sitting on ten years of history… Because [unintelligible 00:10:49.09] Angular were transplanted from AngularJS, so we really are sitting on the ten years of history, and seeing the usage patterns, both how developers respond to the framework, to its API surface, the concepts [unintelligible 00:11:00.22] but also personally, I was spending hours and hours… I was the profiler, so seeing how real applications performed for the users.

So we had this mass of understanding of what’s going on, how the framework is used, and how it behaves on the internet… But there were so many things that we could think of, like “What can we improve? What can we do?” And there is always the temptation to start with the visible surface, like “Oh, maybe this API is not great. Maybe we can just remove one parameter, or remove this function call”, and so on and so on. But I think we resisted this temptation of doing cosmetic changes, and said “Okay, let’s make sure that the foundations are very clear that this is something we want to build on.” And as Alex was saying - this fascinating story that the framework was built, again, based on the same ideas for mapping the data model to the DOM as AngularJS, right? You had this idea of dirty checking… We changed between AngularJS to Angular significantly. For example, we do one change detection round, instead of trying it ten times. But fundamentally, that was the same idea, that the framework doesn’t know anything about your data model. You can use any JavaScript objects, you can change those JavaScript objects however you want, and the framework will figure it out itself.

So that was the foundational really thing for the framework… Yet, what we’ve noticed is the community, or this large part of the community uses the framework very differently. They would plug state or events coming from RxJS into the framework. And that was very disconcerting for us, because the framework was built on this idea that the framework doesn’t know anything; use regular data objects, I will figure it out. And people are coming like “Hey, I’m going to give you this very structured data, with very precise notifications that I’m sending you… Like events, and here’s this part of the UI is changing”, and we were not making any use of these notifications, because the framework will come and say “Hey, it’s great that you told me about the parts of the UI that you changed, but I’m going to discard this information and start the data checking process from the top.”

So there was this very strong disconnection between how the framework was designed or thought of at the beginning, and how it was actually used. And I think being on this crossroads for longer wouldn’t serve us, nor the community. So in this sense, we were kind of – I won’t say forced, but there was a moment where we had to make a choice.

You just had to make the spoon a fork, right? You were using the spoon like it’s a fork, so like, “Let’s just give you a fork”, you know what I mean?

Yeah, absolutely. Just reconcile how community and the framework authors think about the tool that we are used to [unintelligible 00:14:00.08]

[00:14:02.26] I love this story, Kball… The story of using real world data, in the wild, looking at how applications are actually being used… And Angular has the benefit of history, and adoption, and legacy, and it has all this rich – just a rich tapestry to pull from when you’re looking at user metrics in the wild, unlike many other applications. So lucky you. But then using data to figure out “Okay, well, clearly there’s a disconnect between the way people are using and managing their application state, and the kind of shoving stuff into the framework in ways that weren’t designed.” So I love that you went back to the drawing board and really tried to come up with a solution that really, I think, is so simple, so elegant. And it seems like it’s also just something that’s being echoed in other libraries, and other parts of the community, web community at large… So it’s just – great backstory.

Pavel, you said something that I thought was – the two of you said something that I wanted to connect between it. So you said, “Okay, we were looking at the data, and were like “Okay, they’re using this in a different way than we anticipated. We need to better match that.” And Alex, you said “Oh, Signals was a solution that checked all the boxes.” There’s a gap in between there. How did you go from what you were seeing in the data, to “Here’s the boxes we need to check with this new solution”? And what were those boxes?

So just before this, I think the data topic is interesting, because on one hand we’ve got very objective data; performance - you fire up a file, you get the numbers, it’s fast or slow, we compare it with other frameworks on the same page, are we doing better or worse? The interesting side of data is all the issues or bug reports or all the feedback that we are getting from users… So people will often come to our issue tracker and say “Something doesn’t work. Fix it.” And we were looking at this bug like “Hey, we cannot fix it, because it just doesn’t fit our mental model.” So it clearly was indicating the disconnection between those mental models that we as the framework authors and users had.

Then other things were happening - people would open a feature request like “Hey, I want to do x”, and we would look at it and it’s like “Hey, no. I mean, this just doesn’t fit into the design of the framework.” So there’s this whole area of data that you have to look at the mass of issues, and just trying to understand what people are trying to do, how they’re using the tool, and what led them to asking for a very specific things… To which we could just say no. Like “Yeah, this doesn’t make sense in the framework. Just close, and done.” But I think we invested a lot of time into take the next step, and then like “Okay, what was the intent of the user?”, why they thought about it this way; how the confusion happened. And this is much less something you can put in the rows and columns and say “Okay, it checks out or not.”

Yeah… Amal mentioned something, which is that you really like that we’re so data-focused, and that we have this rich history of ten years of user feedback, ten years of interactions… And we recognize and like to talk about that; that’s a privileged position to be in in the web space. There are not many web technologies that are ten plus years old. It tends to move fairly quickly. And the other advantage that we have here is Angular is a Google-sponsored, Google-built open source project, and it’s used fairly heavily within Google as well. There used to be 3,000 applications - I’m not sure even the number today; I haven’t checked in a while… But thousands of applications, millions, hundreds of millions of lines of Angular code that we have direct access to, and can not only look at ourselves, but interact with the application developers and ask them “What’s working for you? What’s not?”

[00:18:01.12] So we really have all of this information coming in about the framework and how it’s used, and the kinds of problems people are having with it. So Kball, you were asking how did we go from what the problem is to what are the requirements for the solution… And Pavel and I met fairly regularly for the better part of a year going through this exercise; starting with the problems, and building examples that would fall over, and building prototypes, and looking at other frameworks… And even going to Academia and papers that are published on not only this functional reactive programming, but also the self-adjusting computations. We’re not pioneers in this space. There’s been a lot of work done before us.

I think both of us were fans of Solid.js and what Ryan Carniato is doing, in terms of evangelizing - I think they call him the CEO of Signals. And we started sitting down, like okay, what do we want out of this thing? We wanted a small, composable set of concepts. That was pretty clear. We wanted a notification mechanism, so the framework would know where data was used, as opposed to where data was changed… And we wanted - yeah, a small composable set of concepts, knowing where things were read, and we wanted something that was not time-based, where you’re not listening to events, you’re watching data change. That’s kind of a big split in the Academic representation of these topics. There’s systems built around events, and systems built around values, and we knew that we wanted to be in the values space.

One of Pavel’s favorite sayings is “There aren’t 10,000 ways of doing it.” When you really list out all of the options, there’s just a handful of different approaches here, and Signals kind of stood out as the most mature and the best fit. And If you look at what other frameworks do - Solid is built on Signals directly. Vue is built on Signals internally. The ref reactive mechanism is very much Signals-based. And Preact just announced that they were doing Signals… So you see the web ecosystem converging around this primitive, largely for the same reasons.

So I guess it’s probably worth – this is probably a good time for us to maybe just define what are Signals on Angular… And more so just because I myself am confused about this, because there’s way too much news in JavaScript land… And I’m saying this as a host of a popular podcast. Could you give us the timelines? Because what’s happening with Signals is we’re seeing these echoes in the community - and we see this a lot in the web space, right? Good ideas get traction, they get legs. And we know that everyone’s influencing everyone; popular to contrary belief, framework authors are very much in this renaissance cafe mode, where people think you aren’t talking to each other… Quite the opposite. So I’m just curious, can you define what are Signals, and then give us some timelines of the influencing?

Right. It’s kind of funny, because there’s this whole wave of interest in Signals, and it sounds like it’s something completely new, and “Oh, why all of a sudden it’s here?” and so on. But looking back at how we thought about it, and the exploration with it, it’s almost like there was no other choice. And I’m saying this because I was talking about the split in the road. We could either double down on dirty checking philosophically. Like “Oh, maybe let’s improve Zone.js.” Really put up front this benefit of Angular where you can really use any data model, and so on. Or we want to double down on those reactive primitives that deliver more information, that can help us update the UI in a more efficient way. And looking at the applications that were big applications, that were successful, we saw them being really written with these reactive patterns in mind. And quite frankly, we did like the mental model of it.

[00:22:14.04] So that was the first choice. We wanted to have a system that can notify the framework that something changed, and what changed, and who is interested. And I think what Alex said is pretty crucial. With those reactive systems, we can track who is reading the values, and who is changing them. So we’ve got perfect information that “Oh, you updated the user name. And by the way, it is displayed in this part of the UI. So you can just go to this part of the UI and refresh it.” So that was the first big choice, reactivity versus dirty checking, just to simplify.

Can we double-click into the dirty checking thing a bit? Because - again, in the spirit of people who are not coming from the Angular space right now… You know, Angular was famous for two-way data binding back in the day. And literally, the words used were “Magic. This is magic.” You remember, when Angular 1x first came out, it was just like “Oh, my God, I just type in this inbox, and this other thing updates at the same time”? Because it was like three lines of code to do that. So can you just double– like, what is dirty checking?

Right. And I think Angular always talked about dirty checking [unintelligible 00:23:22.22] in the documentation, or we talked about it… But that’s not the only framework from this family. I think internally, we often talk that there are just two families of the framework: the dirty checking ones and the reactivity ones. And we can go into this… But Angular and React are pretty much similar in terms of the core of the engine. And the basic idea is that the framework works with incomplete, or almost no information what you’ve changed in your data, and still kind of goes about displaying it. And since you work with very limited information, the only way forward is to “Well, I will look at the entire UI, and see what changed.” In Angular we call this process dirty checking; in React it will be VDOM diffing. But the essential idea is that you basically look at everything that might change, and you compare what you’ve seen before to what you see now. And if you see a difference, you update the UI.

I always compare this to the games we play as kids, where you have two pictures, and things are slightly different between them, and you’re trying to find the 11 differences…

Spots the difference, yeah.

Yeah. The framework is basically doing that with your data when it’s going through change detection, or when React is going through VDOM diffing. It’s like “Okay, you’ve told me you changed something. What was it? We’ve got to find it, so we know where in the DOM to go update.”

And there’s lots of clever data structures you can use to reduce that computation time, but it’s never zero.

Right. And I think what we realized was those systems - and again, that was reflected in the real application… It’s that in the dirty checking systems there is potentially a lot of guessing to be done. You want to go through the large parts of UI to figure out if it changed or not. And the only way you can speed it up is to skip parts of the UI. So the way of getting better performance is telling the framework “Hey, just don’t look there. I know that it didn’t change.” And that’s a very different philosophy from a system which tells the framework exactly what’s changed. So we started to talk about it like performance as opt-in; you need to say “Well, that’s fine. Don’t look at this part of the UI”, versus performance by default, so to speak, because you tell the framework exactly “That’s fine. I know that only this part happened. You don’t even have to bother with the rest.”

[00:25:52.22] Yeah. There’s something beautifully purist about the idea of “Oh, everything is downstream of state, and so the framework will take care of that magically, and I don’t need to think about which things changed. It’s just a pure representation of my state.” But as you’re highlighting, that doesn’t scale. And so that abstraction starts to break down when you hit certain performance levels. You’re like “Well, it’s a reflection of state, but I’m going to tell you that state didn’t change, and this state didn’t change, and this is where it is”, and so your beautifully pure abstraction gets messier and closer, and closer and closer to reactivity anyway.

Right. And we can think about it as caching. If you look at the memo from React, basically the second argument is the cache key. I’m just telling you “Oh, you don’t have to run this computation”, and I’m giving you conditions under which you can cache things. So that’s the kind of defense mechanism. You just basically add caches here and there.

That was really excellent framing, Pavel. I just want to say thank you so much. You’re right, there are really two buckets. It’s frameworks that do dirty checking, and frameworks that are more reactive, or are reactive. So thank you for that framing. No pun intended.

Yeah, I think that was a great breakdown of that mental model. Let’s do the same thing for Signals and reactivity. What is the mental model of how this thing works?

Yeah, so there’s a number of different analogies, I think, that we’ve used to try to describe it over this time… But the one that works for me is a signal is very much a variable. It’s a box that you can put a value into. And you can read it like any normal variable, you can look inside the box and take the value and say “Okay, this is the user’s name. I need to display it here.” But this box - you can change the value in the box. You can say “Hey, I need you to put a new value in here.” Or changing the name. But this box has a very special feature, where it has a little antenna on it, and when the value changes, it can broadcast a little notification that says “Hey, you might need to pay attention to what’s inside me.” And if you’re reading this box, you can kind of subscribe to those messages, look for those notifications, and know when you need to be updated.

So Angular, when it’s rendering a template, and reading the values and your signals inside these boxes, kind of knows which boxes it’s read, so that when one of those things change, it can know which parts of the UI need to be refreshed, and which parts haven’t changed, which parts can be left stable.

Is that like a build time thing, a runtime thing…? Where does the initial “I know where all the things are” happen? Where does that happen?

Yeah, so I think the funny part about Signals, that when it clicks it’s such a simple concept, in the sense that there are very few moving parts… So we will have to work with many different mental models, because different mental models will work for different people. So this one mental model that we use is exactly the one that Alex was describing. The box for the values that – value plus [unintelligible 00:29:00.26] So I’ve got the box, I’ve got the other box, and so on. Then how do we connect those boxes? How one piece of state is paying attention to the other piece of state - it’s all runtime.

The other mental model is really what we are doing at building a graph at runtime. So each piece of state is represented as the value, and then if I want to derive new values from the existing pieces of data, I can just add another node in the graph, and so on. And the act of building this graph is happening behind the scenes. The edges of the graph update when you access a value, when you open the box to look what’s inside.

[00:29:50.29] So from the pure computer science point of view, it’s really a graph. We’re just building a graph every time we access a value, we read a value. And when this graph is constructed, we can just basically use the edges to propagate a change notification. And so doing an update on one of those boxes, or throwing a new value into the box; I can go back to the graph that was created, and I can propagate the notification all the way down to the UI that is actually displaying it. So I know very precisely like “Oh, this tiny bit here needs updating.”

Yeah, it’s like extreme precision brought to you by graph theory, you know… And also, graph makes sense in terms of a data structure, because it can infinitely scale complexity-wise, without compromising on performance too much it. It’s got a nice perf scale chart, which is great, too.

Right. As any system, it has characteristics and trade-offs. When we’ve got this graph, it’s fantastic, because it can tell the framework precisely what changed, and then the UI updates are very fast. On the other hand, we have to build this graph, so that can be seen as the overhead. So nothing is free, but I think the trade-offs are on the right side.

And when you are in the application updating state, as you mentioned, the cost of doing that scales with the amount of things that you’re making dirty, and not necessarily with overall sizes of this graph…

Right, exactly. That’s exactly it.

…which is a very nice property. Unlike something like dirty checking, where if you have no other information, you have to go through the entire application, and verify that that update didn’t change every single piece.

Yeah, absolutely. Somebody creates seven new components, while your VDOM diff checking just went up by those seven components, you know? Whereas this feels much more like - yeah, we’re focused on the signals , so to say… The signal variables.

Just to check - this has completely replaced the Zone.js dirty checking, or there’s still – like, if I have an old Angular app, do I have to update it all into the Signals world, or what’s that migration path?

Ah, yes… So our thinking here has actually evolved quite a bit as we’ve gone through this project. And Angular has always had this mechanism of optionally giving us notifications, very much in the style of like you don’t have to check this part of the graph, right? You could opt into that as a component with the on-push mechanism, and then tell us “Hey, no, I actually do need to be checked.” And we focus pretty heavily on backwards compatibility, and not making these hard breaking changes of you have to rewrite or rethink parts of your application in order to update from one Angular version to the next… So we’ve been focused on how can we make zoneless accessible to applications without having to go all in on Signals. That experience looks it will work rather well. You’ll be able to – as long as your code is compatible with this on-push model, as long as you’re giving us the right notifications when you’re changing things, even if those aren’t coming from Signals, you’ll be able to take an existing Angular application and turn off zones, and run without that piece in place.

Signals bring a few things on top of that. First of all, signals are an especially nice way to deliver that notification. It’s a lot less overhead and a lot less needing to remember how to update things, to just set a signal and everything propagates correctly. And that’s because the existing notification is based on where – you kind of have to do this job of knowing where things are used, so you can notify the right components. Signals take care of that for you. Also, the information we get from Signals is more fine grained, and so we’re able to optimize the resulting dirty checking operation much better when we do that. So the short answer is no, you don’t need to be using Signals to experience zoneless, but you will have a better and safer experience on zoneless with Signals in the mix.

[00:34:13.27] Now, is that an application-wide switch? Is it something where you can say “Well, this set of components is zoneless, and this set of components, I’m still relying on dirty checking?”

To turn off Zone is an application-wide decision, because Zone either as a library kind of has to be wrapping your application or not. But we do have a feature in the pipeline called Signal Components, which are components with extra restrictions and guardrails, that can only update through Signals. And Signal Components are kind of by default opted out of the global pass of dirty checking. So you can start shifting your application onto the Signal feature, onto the Signal world, and make that global operation smaller and smaller as you kind of convert more and more of your code.

So that’s a super-opinionated decision, and I’m eager to hear from you both on this. That’s how I would use a linter, for example. I would write a custom linting rule to say “Hey devs, stop it. Stop, stop, stop! Do it this way.” But you’re like “No, we’re just gonna roll this into the framework”, you know? So what was the decisioning and motivation behind that?

So I think in all the discussions and all the new features that we are planning, for changes that we discussing, there are two voices in the discussion, coming from two different corners of the room. One, some people would advocate for the current applications. “I’ve got huge applications, years of investment, big teams…” And what those things are asking for is gradual improvement. They want the framework to evolve, but not break their applications.

But then there’s the other part of community or voices like “Hey, tomorrow I’m starting a new application. I don’t have any baggage. I just want all the goodies, even if they are as breaking as you want.” So there is a big balancing act between those two voices, especially that sometimes they convert into one, because I might have a greenfield application, but I want to use existing libraries, that were written with a different set of assumptions.

So there’s a lot of just thinking of how we get the ecosystem [unintelligible 00:36:26.18] without fragmenting it. And I think that’s kind of also reflected how we are rolling out Signals. And this is probably a whole topic on its own, but essentially, we want to make sure that all those new features are accessible to the existing applications as well. And if there is a bigger split or a bigger jump to make, we want to make sure that there is enormous benefit of doing it, that you almost feel like “I want to invest this time, because the benefits are there.” But gradual improvements, and bigger jumps, where the value is so obvious.

Okay, so I as an engineering manager, I love that answer so much… Because it is one that is really conscious and mindful of engineering time and engineering spend. And churn. Right, Kball?

Oh, my gosh, yes… No, I’m hearing this, and I’m like “Ah… These people are thinking –” And it gets to –

It’s almost like there’s real adoption, you know? Real adoption of the framework in the wild… [laughs]

Yeah, I mean, I think it gets to one of the cruxes or challenges that you get into – like, we talked about the benefits of ten years of data. But one of the challenges of having usage is the inertia that is built up. And you have the whole “Don’t break the web” thing, where browsers can never drop an old feature… Well, there’s some amounts of that, and Angular actually has a little bit of a history of not being as cautious about that upgrade path, early in its existence. So it’s really nice to see y’all being like “Wait. Incremental change is an important piece of this.”

[00:38:10.25] Totes. And this is not just a principle for folks who are listening. This is not just something that library authors should be doing more of. This is actually a way to engineer safely when working in large-scale applications. Like, how do you refactor a legacy application from within? Martin Fowler has this really great strangler pattern, where you replace things one module at a time. And there’s lots of – like, working on a car engine that’s on, it’s very different than turning off the car, taking out the engine, putting it in the shop for six months, nobody’s using that car, nobody’s getting value out of it… It’s very different. And I feel like so often we’re so quick, especially in the JavaScript community, to throw the baby out with the bathwater. And we lose that really important muscle and skill when it comes to “How do I refactor? How do I make thoughtful decisions? How do I come up with a roadmap that’s gradual, and safe, and pushing to production incrementally, without completely taking us out of commission for six months or whatever, where we’re not delivering value, or we’re re delivering value?” So I just super-appreciate that, really. And I know how hard it is.

And I think you kind of both touched on two interesting topics. One is that gradual improvement is great. Again, I think we cannot just do that, because as you mentioned, there’s inertia. If we just only do very small, gradual changes, then the framework will just – we kind of need to pull the baggage, so we’re moving slower than the greenfield frameworks. So Again, I think we want to jump on bigger problems that are worth solving, but again, it means that – you know, i there’s effort on the application side, the value needs to be obvious. So that’s kind of - I wanted to highlight it.

The other interesting aspect of managing change and this transition is… There’s one thing to do pure code transformations, and we actually can automate a lot of it. I think Angular is pretty famous for all the tooling that we provide with releases that shifts [unintelligible 00:40:21.22] your code to the new APIs. What is interesting to see that it’s not enough because it’s one thing to change the code, it’s another thing to change how people think about the framework, how they teach it, and so on. So there is also this aspect of work that we are doing, and collaborating with the state management libraries very closely, to make sure that if there are new primitives, they can actually be used in the popular libraries; that we are just not “Oh, hey, Signals”, and “I don’t know how to use it.” So there’s a lot going into it.

Can we go a little deeper on that? Because I had in my notes to talk about the relationship of Signals and RxJS, which I know is hugely adopted in the Angular community, and not that much outside of Angular… So we might also need to diverge slightly into talking about observables and what are they… But how are you managing – well, first, how have you been thinking about the relationship between Signals and this other framework for essentially managing notifications about things happening? And second, how is that relationship, and sort of evolution happening?

So observables are an async primitive. They’re implemented in a lot of different varieties, but one particular flavor in JavaScript is the RxJS library. And observables are essentially event emitters. They’re this primitive that’s able to give you a notification when something happens. And in particular, they’re, I would say, lazy or cold event emitters. So if you don’t subscribe to them, then whatever action that is going to produce the value eventually is not actually running in the background. So you make an HTTP request with an observable - it’s only when you subscribe that the backend request actually gets fired off, and eventually you get the notification back.

[00:42:11.10] They have some very interesting properties around composability, so there’s a whole set of operators for transforming these streams of values over time, and combining them, debouncing them, filtering them, mapping them, flattening them, catching and dealing with errors, and things like that. And Angular is famously of two minds about RxJS. Some parts of the framework are integrated in a more tight fashion. Our HTTP client returns observables, our router deals in observables… And other parts of the framework, such as the core change detection mechanism and component inputs are not using observables for notifications. And that’s always kind of frustrated some people, and in particular in our developer surveys we get - about 50% of Angular developers say “Please, I need more RxJS. Can you support these extra things?” And 50% of our respondents say “Please, no more RxJS. I don’t want to have to deal with this thing.”

Yeah, so RxJS is one of your options if you’re thinking about building a reactive system. It’s not in the same category as Signals. So the big difference is signals are values that can change over time, and RxJS observables really give you notifications of events that are happening at a specific point in time. And that event might be a value changing, but the way these things compose is very much driven by that distinction.

So a signal - you can always ask a signal “What is your current value?” It will always have an answer for you. Observables - that question doesn’t make sense. The observable doesn’t have a current value. It only has the next event that it will fire at some point. Maybe if you subscribe to it, it will give you a value, but that’s not a guarantee.

So just to bring in another set of things here… If I’m understanding correctly, if we were to think about - observables are an event stream; if you’re an event-based architecture, then you are flattening that into views of some sort. A signal is more like the view. It’s like “This is the current state of things, and the connections of where it’s going to.”

Yeah. Again, just to say differently what Alex said, because I think maybe different words will work with other people… But that that’s the crucial – we are often saying “Oh, events versus values”, and that kind of sounds abstract… But the good way, or one way of thinking about it is let’s say I’ve got this one primitive, just a value in a box. I can always look at this box, and I’m going to get the value, so I can safely assume that I don’t have to check for the null values, for example, undefined and so on. If I subscribe to the app event emitter, I’m just subscribing; if there is no event fired, there was no payload, I don’t have a value. So now all of a sudden, I need to deal with undefineds everywhere. And I start to compose - let’s say that’s fine. You can say “Oh, well, checking for one undefined in a template - that’s fine. I can do it.” But then I start to compose those observables, and maybe all of them I need to assume that they will come with undefined values. And it’s one bad apple is spoiling the whole basket, right? I always have those things, like “Oh, I don’t know. Maybe I have something to display or not.”

The other crucial aspect of this is the guarantees around what happens during the subscription. In Signals, I can look at the value. It’s inconsequential. No one noticed. Or almost no noticed. In observables, the act of subscribing is very consequential. In the case of cold observables, I might be – I don’t know, starting a request to God knows where, and I don’t know, all sorts of side effects can happen. And on top of this, each new subscription might start those side effects.

[00:46:10.03] So something that starts on the surface looking very similar, in practice has very different consequences. And I think we kind of collectively need time to see those differences in practice… But both - Alex mentioned in Academia, and in papers, those concepts are very clearly distinguished. And I think we’re just in the process of starting to appreciate those differences.

And I would say that Angular in particular has a rich history with RxJS… And we’ve seen both how it’s enabled applications to scale bigger and bigger over the years by using RxJS as this driver of change notifications… But we’ve also seen how it’s continually frustrated new developers who are coming to the framework for the first time, to feel like they almost have to become an RxJS expert before they can start to write Angular code in the way that they’re being told that it should be written.

So we’ve narrowed this down to RxJS while being a fantastic orchestrator of asynchronous operations for the kind of event streams that you are working with when you’re doing data fetching, and in particular more complex patterns, like autocompletion, and debouncing, and things… But RxJS is a poor fit for state management, for keeping track of what the current state is, and delivering notifications about that state to the UI, because of what Pavel was saying - you always have to deal with this fact that it might not have a value, or it might create an error, or it might have a side effect if you try to look at it. So we really had this fork in the road…

You can also have unexpected values as well, depending on when you’re observing…

Yes. And in particular, with an observable, producing a new value, so when the sending side says “I have a new value for you”, that execution of your subscribers is synchronous. And so you start to depend on the order in which these things are giving notifications, and not just on the final state. And we’ve seen that lead to really subtle and confusing behaviors in applications. Some very large applications were very surprised to see how many executions their subscribers were having, because they were observing all of these intermediate states that they didn’t intend to be.

Yeah, observables are very powerful… And the patterns that RxJS amplifies using observables - they’re very powerful. It’s like using a Ferrari for every single kind of problem, and you don’t need a Ferrari all the time… And I feel like Signals is that good in-between spot, where it gives you 80% of what you can do around managing and architecting reactivity, but when you really want the big guns, you can still reach for them. But the reality is you don’t have to anymore, and I love that. It’s just now there’s something built into the framework that’s just very simple, that allows you to manage your state… And all kinds of things, and just the way that it is very, very simple and intuitive. And I have to put my hand up to being one of those developers that - you know, for me, one of the barriers to entry I’ve found with Angular, the newer versions of Angular, it’s just “Whoa, it’s very kitchen sink.” So on the one hand it’s this fantastic, robust, big platform that solves all these problems for developers, problems they didn’t even know they needed solved… And then the other hand is like “It’s going to take me two years to become an expert in this thing.” And I feel like even if I come in as a newbie, sometimes I feel like I need to know all the history behind the 10 years of decisions to really be a true expert, you know? So yeah, that’s tough. That’s a tough place to be.

[00:50:01.00] That’s a very varied table. We’ve heard those stories over and over again, people saying “Okay, I see the power of this machinery, but just holding it is just - it’s effort.” And I think the other interesting aspect that we touched upon here is that RxJS or those patterns are not that popular in other frameworks. And I don’t want to say I’m surprised, but it is fascinating for me personally, because people that are much into RxJS, they will come to us and say “Hey, but there are so many patterns around orchestration, or cancellation of things, that if you don’t RxJS either you write a lot of code, that is pretty much - you cannot digest it after you wrote it, or your application has bugs. And RxJS is the perfect tool.”

So we are trying to understand it… Maybe the Angular community works on those applications that those complex interactions are necessary, and we want to keep RxJS as the tool for those complex scenarios… But there are probably many applications that will just fire a fetch request, get the data, and work with this data. And there’s nothing to orchestrate, there’s nothing to cancel. And as you said, giving those people very powerful tools - yeah, you kind of give them a machinery that is more expensive to operate [unintelligible 00:51:23.03]

So you mentioned a little bit there about “Oh, maybe the Angular community is working on these more -” or something… So I want to use this to transition us to talking about that community a little bit. And you’ve talked about - you have people who are saying “RxJS for everything”, and other people being like “Please, no more of this.” You have a lot of different types of folks using Angular. How do you think about that community? What goes into it? What does that look like?

I’m particularly proud of our RFC process for this reason, because we know that we are, first of all, not representative application developers. Various people on the team have experience having built applications in the past, but we are not users of the tool on a day to day basis, to the level that people in our community are. And so every change that we make that is of this scale, of like we want to think about Signals, or we want to think about a new control flow syntax for templates - we write up as a proposal and have a discussion with our community, and solicit all of the feedback, all of the information.

Pavel and I often talk about, like, the thing that we are most in search of is new information. Something which is going to change our minds, or adjust our thinking on a topic. And so when we post RFCs, we’re really looking for that one person who comes in and says “Oh, actually, this thing doesn’t solve my problem, because I’m working on a special kind of application, and it has these needs. And if you take away Zone.js, then my thing can’t work anymore.” Those are the kinds of points of feedback that are really consequential, and that help shape our decision-making.

So I think every tool, every [unintelligible 00:53:09.23] the framework is creating communities, and I think we all tend to think that our communities are special. And I do believe that Angular communities have something special. And I think there are a few ingredients that go into it… There’s, let’s say, the official part. We’ve got a lot of possibilities that bring people together. We’ve got dev rels that their whole job is actually engage with the community. We do have a GDE, a Google Developer Expert program where actually members of the Angular community act as the ambassadors of the framework, and being this translation layer between the team and the larger community. So we do have those kinds of things designed to facilitate, and Alex mentioned the RFC… But I think there is more to it. And that’s pretty personal to me, so I’m sorry if it sounds cheesy, but I think we are trying to be, or at least I’m trying to be approachable in a very human way.

[00:54:11.21] We can talk about technology – we often use the term “There is no technology religion, there are just lots of things to juggle.” And as I’ve mentioned, we sometimes don’t see the things that you see very clearly. I think at one of the conferences we had T-shirts “You can sit with us.” That was kind of the manifest, of like “Hey, we’re actively seeking those interactions.”

And another aspect that is important for me is recognizing the fact that working on applications or frameworks is your job, sometimes it’s your hobby, or maybe it’s part of your university… It’s a huge part of your life. And that’s a huge commitment on people’s side. And emotions come into play. After the whole week of working on something, it’s Friday afternoon and you’ve got that bug to debug. That can be super-frustrating. So there is this deep recognition of the fact that we impact people’s lives, if we want it or not. And sometimes people will come to us with their lives, with their frustrations; they will come with the good things, but sometimes they will be like “Hey, your tool is rubbish.” And I think it’s part of our job to try to remove those layers of emotion and understand what happened.

Do the product management work and try to understand “How do I smooth over these edges?” I’ve said this on the show many times, but I’ll say it again… Angular communities, in my opinion - hands down, the best web community. Y’all have managed to, in the words of Justin Fagnani - I’m sure you know him; author of Lit - he said that you do a really good job of creating this big tent feel in your community. And I would agree with him on that. It always feels family. It’s like the cult of Angular in the best of ways. I don’t even know how to explain it. Angular people love Angular, they love the Angular community, and they’re good to each other, they’re kind to each other… All about learning, and teaching, and knowledge sharing… And Signals significantly reduces this cost. But I think for me it’s a pretty high cost entry to become an Angular expert. And I think without that level of engagement from the community in helping uplift everyone else, I don’t even know if it would be as successful as it is. So just really, congratulations… And obviously, you made some really big bets with TypeScript about a decade ago… I think that’s what the meetup that I was helping – I was the company representative at that meetup, so I was wasn’t purely hosting, but I was sort of co-hosting… That was kind of the big reveal. That was the discussion, or one of the discussions was maybe perhaps on TypeScript… And that decision was quite big.

So I don’t know, very forward-thinking in so many ways, and I can’t wait to see what the next 10 years of Angular look, especially for your large enterprise apps that are aging… I’m so eager to see this these large, rich JavaScript applications get updated. But I personally still have a lot of questions and thoughts on RX, and compute, and… My head is still in the discussion from 20 minutes ago, but…

We could do a whole other episode on this.

Oh, my God…

Well, and I do want to get – and we need to wrap up shortly, but I want to get a little bit into that future-facing… So not looking at all of Angular; and actually - short plug, we’re going to have another episode with other folks from the Angular team, looking at the future of Angular, just in a few more weeks. But thinking particularly about Signals, what does the roadmap look like for you? What’s coming down the pipe?

[00:57:50.26] Yeah, so it’s actually public. On our GitHub organization you can go and look at our project backlog for Signals, and see exactly what we’re working on. But to kind of summarize, we are focused right now on landing all of the pieces of the RFC that we held about a year ago, I think, on Signals in Angular. And signal inputs was very recent, signal queries is upcoming… Pavel, I think, has that change pretty much wrapped up at this point. We have model inputs, and then the parallel story arc of the zoneless experience. So we’re working on what the zoneless looks like for applications, what the zoneless looks like for testing, what does zoneless look like for server-side rendering, where they’re a whole separate area of concerns. How do I opt into this? How do I migrate? How does Angular help me make sure that my application still continues to work in this mode? There’s Signal Components, which is kind of like the marriage of Signals and zoneless, adding the optimized change detection, the guardrails around making sure that you’re not able to create performance problems, or to have components that don’t update correctly.

Beyond that, I think we’re really excited about some of the Signals in server-side rendering space; how can they feed into things like partial hydration, exploring ideas in the space of resumability… I think there’s some really fascinating opportunities there. I don’t know, Pavel, did I forget anything?

No, I think just maybe another way of looking at it - so there’s the whole backlog of tasks, and there are just lots of tasks, but if we put them into buckets, I think we’re kind of rolling Signals in a few stages, right? The first thing we built - hey, the core library got this reactive primitive. You can put value in boxes, have fun. That was the first step. From there we are gradually making the connection between the framework and the core libraries. These connections are bidirectional; on one hand, we are using notifications from Signal, so we can do rendering more efficiently, so we can talk or speak in signals in the framework. This phase is kind of done, and we are in the next phase, where we are enriching the framework so we can respond back to you in signals. We recently rolled out inputs as signals, so props as signals. So the data incoming to the component can be represented as the reactive value. Alex mentioned queries - this is kind of the next thing in the works.

And then when it’s done, we want to use that – and we talked about it as the guide rails; we kind of introduced a new way of working with the framework. We want to have signal-based components, with like “Hey, this is the best set of rules that we can think of at the moment.”

So this would cover the core framework, but it’s not done, because Angular being the whole ecosystem, we’ve got internal packages… So there are great examples of the packages we could update to Signals. Forms is the prime example. So that’s probably what we’ll look into after the core of the framework is tackled. And then we can expand. There is architectural patterns around – and managing state on the large scale, and I think we will do this in the partnership with the state management libraries, and we do have a special forum where we engage with those people. So there are layers of onions…

Just to add in, one of the kind of ancillary pieces that I’m most excited about is dev tooling. So we have the Angular dev tools for introspecting applications… And Signals and dev tools is a huge opportunity to make the debugging experience and the development experience really smooth, being able to set breakpoints on signals, or see the visualization of the graph, and see how the data is flowing in your application I think is really powerful.

Super, super-powerful. Yeah. I think part of Redux’es success was Redux Dev Tools alongside it. It really made that wrangling of data – and actually, the question I wanted to ask you, coincidentally, is I’m curious how does NgRx, which is the Redux flavor of Angular… Angular’s kind of Redux-friendly package for doing state management… How does that fit into this world of signals? Does it matter? Can you use both? I’m assuming yes, but I don’t know. You tell me.

[01:02:15.27] Yeah, so pretty early on after we made the internal decision that we want to do Angular on Signals, we from the start recognized that this will not be successful unless it is adopted and embraced by the state management community in Angular. And so we’ve reached out to a variety of different projects, including NgRx, and said “Hey, let’s chat. Let’s have a forum, let’s have bi-weekly meetings, and let’s explore some of these ideas together.” And so we’ve been engaging with them since long before the RFCs even went out, kind of sharing early prototypes of signals with them, and getting them involved in the discussions.

And so they’ve been working, all of them, on how to integrate signals into their existing offerings. And so they’ve been trying to – basically solving the same problem we are, which is “How do existing applications that are using the current thing kind of incrementally migrate to the new approach?” And so there are ways in NgRx now to kind of get signals out, instead of getting observables. They have the signal store, which is a reconceptualization of the component store built on Signals from the ground up. So we’re partnering with them to solve that very problem, to make sure that it’s a smooth transition for applications that are using NgRx to start using signals, too.

Totes. Yeah, that’s amazing, and strategic, and awesome… And I’m sure it’s going to be a win for both projects, to have that Integration and support, and making that more seamless for people that are incrementally adopting those changes. So I’m just super-glad to hear that.

Awesome. Well, Alex, Pavel, thank you so much for your time today. Do you have any final thoughts you’d like to leave listeners with before we call it an episode?

I just want to say that that’s the evolution of the framework; it will take time as we’ll go through different layers of this onion… But regardless of the stage on which we’re in, I think what matters is your applications and your use cases. So we always want to hear about what’s not working. And GitHub issues is probably is the best, the most accessible forum. But just essentially, I think we want to think about all the developers as our colleagues. We work on the same web space. So yeah, let’s just work together on making the best tool possible.

Yeah. Plus one to that. I would, even if you’re a React developer, even if you’re using Vue or something else and haven’t experimented with Angular before, give it a try. Even if you’re not using it at work, try it out. Give us your feedback, because we want to know.

Awesome. Thank you, Alex. Thank you, Pavel. Amal…

Yeah, it’s been such a pleasure. I’ve learned so much. And yeah, I can’t wait for more Angular content in 2024.

Sneak plugin, coming again in a couple more weeks. Alright, this is JS Party, and Kball signing out.

Changelog

Our transcripts are open source on GitHub. Improvements are welcome. 💚

Player art
  0:00 / 0:00