I've used cycle.js on a small commercial project. I'm drawn to novel non-mainstream approaches, and am happy to put up with teething pains with new technology. However, my experience with cycle.js was such that I couldn't recommend the framework to others.
Central to the understanding of cycle.js is the concept of observables. An observable is powerful abstraction, as it allows event streams to be programmable. However, an overreached abstraction can be even worse than a lack of abstraction. Cycle.js gratuitously applies the observable abstraction to its component model.
The cycle.js notion of a component is a object that takes multiple observable sources, and emits multiple observable sinks. I have to admit, at first I didn't see this as problematic: to the contrary - it actually struck me as elegant. What I didn't realize, before building a real-world project with cycle, was that this meant all interactions between components were asynchronous. So each component cannot even access the state of another component without communication via observables. This introduces enormous needless complexity into an application. Not only do you have to pay an up front mandatory boilerplate tax to minimally wire together your components, you'll tie yourself in knots readjusting that wiring to get those components to communicate with each other in the way you need.
We actually ended up re-writing the project with our own framework, pickle: https://github.com/pickle-ts/pickle For us, this substantially increased maintainability / reduced the code size of our application compared with cycle.js, or the prototype we wrote with react/redux. The code reduction came from realizing the appropriate abstraction: the entire application should be composed of an object-oriented hierarchy of stateful components, where on each update, these mutable components render themselves as immutable v-dom trees. Anyway, to counteract any pluggishness (our framework is obviously super new so is only of interest to ultra early-adopters), the conservative sensible choice for a web framework right now is to use react or vue /w a state manager. Not cycle.js.
I suppose you were using Cycle.js before Cycle State was released. There are a few other projects in production like yours, some of them successfully applying the framework, and others that found difficulties with state management and wiring between components.
For anyone out there looking to evaluate Cycle.js for production, I recommend looking at the source code of an open source app I'm actively building: https://github.com/staltz/manyverse I am happy with the architecture, specially how well it manages React Native's many native APIs in a well organized way.
I also need to emphasize that Cycle.js is more of an architectural framework, not much of a rendering framework, so it's not a competitor to React, you can use both together. React for rendering, and Cycle.js for architecture and orchestration.
React may be superior in ecosystem, or with feature coverage, but not so as a paradigm [compared to cycle-js]... Once you learn Elm or Cycle, getting things done will be more productive, less indirect, less verbose, more organized.
Am I quoting you fairly? Have you changed your position?
That article was written many months/years before Cycle-React interop was built. I still maintain some of those positions because of Redux, but when you take React without Redux, it's just a rendering library, and as such could be theoretically used with Cycle.js. Now that React has good APIs like Context, ForwardRef, and function components, the interop with Cycle.js is easy and well done.
redux-observable has helped me get some of the better parts I liked about Cycle into a more "traditional" React app setup.
For maintainability by other developers I've been migrating my biggest Cycle app into a "traditional" React app. Partly because I got very caught up into some "plumbing" issues in Cycle, with lots of dynamic components and keeping them performative starting being far too much work, especially if I expected other developers to read it, much less touch it. The Cycle/React-interop has gotten better so that I might contemplate React-hosted Cycle components for future needs, but at this point with the vast React ecosystem and increasing Junior Developer-friendliness of "traditional" React, it makes the most sense to just use as much off-the-shelf React as possible and Redux seems familiar to more developers (even if they might not immediately get what redux-observable "epics" are doing).
Every framework or tool will go out of its way to demonstrate the things it makes easy. They usually don't mention at all the things it makes hard. Often you don't discover what those are until you've tried building something non-trivial with it, but there are always tradeoffs.
What do you mean that all interactions between components are asynchronous?
I've built and am maintaining a rather large chrome extension for video recording using xstream/cyclejs.
I find that the thing xstream get exactly right is that stream is an abstraction of Values Over Time and not a queue of messages.
It's a subtle but important distinction. xstream and by consequence cyclejs is synchronous. When a value goes into the stream, all interconnected streams ar executed synchronously.
What I didn't realize, before building a real-world project with cycle, was that this meant all interactions between components were asynchronous. [...] This introduces enormous needless complexity into an application.
This hits the nail on the head. I spent a year using the author's other project - Rx.js - and while I understand the benefits of using it I don't think they outweigh the costs.
Two corrections: RxJS is not my project, I only helped as a contributor (sometimes voluntary; sometimes core). And second: stream chains are not necessarily asynchronous, in fact they are synchronous by default. I don't know what the commenter above meant with that asynchrony comment.
It's misleading to say observables are not asynchronous. The raison d'etre of obserables is to support asynchronous operations, and it's this capability that constrains the programming model.
There are many ways to derive Rx, some involving category theory and appealing to mathematical duality, but this article shows how every developer could have invented Rx by crossing the standard JDK (Java Development Kit) Future<T> interface with the GWT (Google Web Toolkit) AsyncCallBack<T> interface to create the pair of interfaces IObservable<T> and IObserver<T> that model asynchronous data streams with values of type T.
I didn't say "observables are not asynchronous", I was referring to the comment "interactions between components are asynchronous" which to me sounds like "event emission and propagation". Propagation is often synchronous when using RxJS 6 which uses by default the recursive scheduler, and with xstream, which is similar. For instance, event emissions along the chain a$.map (f).filter(g).take(10) are synchronous. But I suppose the confusion here is what people mean with "asynchronous". To me it means a set of computations that are allowed to happen sparsely, with other computations potentially happening in between. A lot of people assume that callback-driven code in JS is necessarily async (and maybe that's what you meant with the use of the word), but it's common in the JS reactive programming community to recognize that callbacks can also be executed synchronously, for instance array.forEach(cb) is synchronous although it's callback-based and as such could have been asynchronous through the same cb API.
Andre - the point is that the inter-component wiring between cycle.js components is verbose and unwieldy due to the asynchronous-capable protocol. Definitions are of course important, but in this case dwelling on them is to side-track a criticism, rather than to address that criticism directly.
With observables, they can be executed synchronously or asynchronously in JS, but it's a bit of a leaky abstraction when you have to be aware of the internals of particular stream operation to known whether it is synchronous or not, so to avoid unnecessary complexity you need to assume asynch behavior for all streams.
Cycle is really cool, and I'd love to see reactive programming become more popular, but after trying it out for a project it has a few ergonomics issues that keep me from using it as my primary web framework.
- Dynamic lists of components are really annoying to create. In Cycle, the happy path is calling every component function in your app at startup to set up the data flow graph, and then your reactive operators handle the actual logic. With dynamically added or removed components, you get a stream of "sinks" and then have to splice them into your data flow somehow.
- Circular dependencies are cumbersome. If a parent passes a value to a child which can change that value in the parent, you essentially have to create a stub stream in the parent, use it to derive the parent's state and then "imitate" the output of the child as well.
That said, Cycle has a lot of great ideas that I want to become more mainstream. If you have a free weekend, try it! The creator André Staltz's blog is also a good read: https://staltz.com/
I totally agree with the two issues you listed! I think both of them are well enough addressed with Cycle State, a new state management library. It has an API makeCollection() specifically meant for dynamic lists, with good DX and great performance. Also I regret imitate(), and gladly using Cycle State I haven't found a single need anymore for imitate().
Not mentioned in the official docs (yet), but Cycle.js has nowadays full support for React DOM and React Native.
Cycle State looks great! I had checked out onionify, but it didn't have anything like the makeCollection() API at that point.
As an aside, thank you for all your work on Cycle! The project I was working on was a visual reactive programming and design tool, inspired by Bret Victor's on direct manipulation and abstraction and by your work on reactive programming and Cycle itself! I remember seeing something like that on the long-term Cycle roadmap as well, but I couldn't find it when writing that comment.
If you're just starting out though (or even if you're not) I'd recommend checking out create-cycle-app, the Cycle analogue to create-cycle-app that will set up things like that for you: https://github.com/cyclejs-community/create-cycle-app
The code looks a bit messy to be honest, maybe it's just the variable naming. `const input$ = sources.DOM.select('.field').events('input')` Why not name it the same as the DOM function `querySelector` ? `const name$ = input$.map(ev => ev.target.value).startWith('')` Wouldn't in this case `name$` be a boolean, but then `map` is called on a boolean. I just find the syntax very confusing.
By convention, anything suffixed with $ is a stream. input$ is a stream of input events on all elements matching the selector ".field". The declaration for name$ says to take that stream of events and turn it into a stream of the value of those events, and to seed it with an empty string as the initial value.
Reactive programming is a paradigm shift and definitely tough to wrap your head around at first. André Staltz (the creator of Cycle) has a good intro here: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
The conventions, naming, etc are not Cycle (or even JavaScript, in most case) specific. It's just straight out of the reactive programming ecosystem.
I think Cycle uses XStream these days, assuming it didn't change again from last time I looked, and they renamed some stuff from the previous RxJS based implementation to be a bit friendlier, but even then the streams are not necessarily about DOM/html. CycleJS lets you do everything with streams, from managing http request or I/O and any other form of environment input. And yes, that includes DOM events, but it wouldn't make sense to name functions in a general API after a specific use case.
The core idea of Cycle is that the user is a function that reads views and emits user input events; while the application is a function that reads user input events and emits views. (Hence, the name "cycle.")
I thought it was written up in their docs, but this video is what comes up when I search for "user is a function":
This framing, that user input and interfaces react to changes in each other, is a nice way to think about interaction design.
Ergonomically, I find virtual DOM composition in Cycle a bit too cumbersome. Stateless components are just functions that return virtual DOM, but stateful ones need the input/output streams woven in. In practice, this means you end up doing a lot of
const statefulComponent1$ = // compose input streams here
To add state to a stateless component, you've got to do a bunch of refactoring to both the component and its callsite. Instead of passing props/function arguments inline to a stateless component, now you've got to make a variable to hold your stream, pass it into combineLatest, and put a slot in your returned virtual DOM to hold its latest emission. I understand why it's this way, but I also don't like having to think about if a component has state when I'm just trying to use it.
Cycle is a nice architecture for Chrome extensions though. They do a lot of message passing between JS contexts. Because everything in Cycle is a stream, you can wrap the extension messaging API in a stream, and your extension architecture looks like any other Cycle project:
The thing that got me excited about React was (and please correct me if my vocabulary is incorrect) the virtual Dom and the algorithm to detect changes and only update the dom that changes. Does Cycle.js save that same work and reduce bugs, or is there a hidden cost without that kind of feature?
To be specific, this reference criticises setState because it does a bunch of implicit updates to subcomponents. I've seen how in practice this does make things hard, but I've also seen the power of it when it works well. Cycle.js looks like it requires me to do a lot more to manage that re-rendering with the benefit of knowing exactly how it is happening.
It is a myth that what I've described above actually saves time and reduces bugs?
I've loved reading Andre Staltz's posts on reactive programming, they are so good. This is an amazing project.
Cycle uses a virtual DOM as well. The difference is that React will call your component functions every time it needs to render, while Cycle will (mostly) only call them once, when the app starts up. A Cycle app is a data flow graph set up by your component functions, with a "driver" at the root observing that data flow and causing side effects, like rendering your virtual DOM or making HTTP requests.
In short: instead of a component being called with props and state and returning VDOM, in Cycle it "maps" a stream of state into a stream of VDOM.
Cycle is very interesting, and Andre is a genius. Even if you will never use Cycle in "real work", learning it enough to build anything at all will enhance your understanding of many things. Particularly around reactivity and state.
A while back I dug in enough to make a short series of videos explaining how some simple Cycle apps work. They are not up to date with the Cycle progress since then, but nonetheless are probably still a decent way to get a sense of what Cycle is like.
[NOTE: I have much more experience using re-frame than Cycle.js, but have used both previously]
Cycle.js and re-frame, in some ways, are on opposite sides of various spectrums.
Cycle.js favors local reasoning and composability through modeling your application as a pure function that takes in streams (sources) and returns streams (sinks). All of your event handling, external data, DOM changes, etc. are explicitly tied together in your "main" function.
Cycle.js requires you to reason about, write and read your application code in terms of reactive "streams" of values. This means that a big part of your app's development is expressing it in terms of operations on reactive streams and using the language of whichever stream library you choose.
Re-frame favors separating your UI code (which you can compose) from state and other side effects (which are global) through it's use of named reactive subscriptions and dispatching of named events.
You register your app's events as functions that take in "coeffects" (e.g. the current state of your app) and returns "effects" as data, e.g. the new state of your application. You can also return other "effects" like the parameters for doing an HTTP request and what event to dispatch when it completes/fails. Your UI code is then written to dispatch the names of these events (e.g. on click) that will trigger the registered function and cause re-frame to run the returned effects.
You register your app's subscriptions as functions that take in the application state and return a materialized view of it. Your UI code is then written as functions that subscribe to the names of these subscriptions, take the materialized view of your app state and return the UI tree that should be shown.
The majority of your re-frame app code is written as regular functions which return data. This means that a big part of your app's development is expressing it in the terms of functions that return data so that the UI framework (React/reagent) can render it and re-frame can do the appropriate side effects.
Cycle.js is a very "pure" framework, in that it's approach is to start from first principles - pure functions, application-as-a-stream - and to layer on tools and abstractions to make it effective. The work that the core of Cycle.js actually does for you is quite minimal, and the skeleton and muscles of your application is not at all hidden from you.
Re-frame is much less "pure"; it has a guiding concept of separating your UI code from the side effects it produces. For instance, many UI functions are not pure because they subscribe to the global app state. And in fact you don't have to use re-frame for everything at all; you can opt to use local component state and direct mutation instead of that fits your use case better.
Another difference I feel is worth mentioning: events in Cycle.js are "pull"-based. Look at how you can select an element in Cycle's DOM tree and subscribe to events to it as a stream.
Events in re-frame (and reagent and React.js in general) are "push"-based. You must be explicit in your UI tree which events we care about and when they are emitted.
I actually wrote a library a long time ago[0] to invert this in React and expose a more pull-based event API, but in practice it wasn't very ergonomic.
That's super interesting. Imagine that as a cljs developer, Cycle and re-frame were exactly as easy to use (i.e., skip the fact that Cycle is written in a different language).
The sticks I would use to measure a framework like Cycle and re-frame are:
1. How well does it compose?
2. How simple are the tools?
Both re-frame and Cycle have their issues with composition.
Cycle has inherent complexity and difficulty with composing dynamic child-states and circular dependencies as outlined above. It sounds like there have been tools developed since I last used Cycle.js to ameliorate the difficulties in handling these cases.
Re-frame has complexity with composition because it uses an absolute global namespace, which makes multi-tenant scenarios very awkward and potentially insecure. This problem mainly exists in use cases like having a developer environment with multiple states of the application on the same page, consuming components developed by external teams, and server-side rendering. At the moment these are either worked around or avoided completely.
The simplicity of the tools is something that I am very passionate about. As someone infected by the Clojure virus, I am pretty excited about the ability to use regular functions that return data to build my applications. It has been several years since I went down the rabbit hole of "everything as a stream/observable." At the time I found it to be very intellectually exciting, but also very confusing. Functions and data are much more familiar tools to most developers and the ergonomics of designing, developing, and debugging functions and data are much more understood.
So in closing, I would choose re-frame over Cycle for a project that I was working with a team of developers on. For extremely large applications that needed to be developed cross-team and/or have a strong use-case for server-side rendering... I would probably pick something new- similar to re-frame, but which fixes it's issues with composition. I maaayyyyy be working on something like that. ;)
Anyone considering cycle should also consider MobX which is effectively "automatic cycle". It's as functional and as Reactive but uses vue-like observables rather than rx-like ones.
`select().event()` does not use `querySelector` directly under the hood, it filtering and routing all incoming events accordingly. So even if you change the elements, as long as they match the selector, they will be routed correctly.
Source: I recently rewrote the DOM driver :)
They were being dismissive, suggesting that this is old news and something they already had on vinyl. As if all we care about is the new shiny and can't possibly be interested in nor discuss existing things.
This is interesting feedback. There are obviously subjective parts in it, but that's valuable all the same.
Focusing on the objective parts :
An observable is powerful abstraction, as it allows event streams to be programmable. However, an overreached abstraction can be even worse than a lack of abstraction. Cycle.js gratuitously applies the observable abstraction to its component model.
Some caveats aside, cyclejs allows you :
- to express your app as a series of equations
- separate side-effects from pure dataflow processing
Whether that is gratuitous or overreached is a question of taste, familiarity and other things, I won't discuss with you on that.
What I will say is that under some conditions, the cyclejs approach actually can make your application much easier to understand that standard imperative workflow processing. For instance, I could write for a super simplified reservation system :
The `=def=` relations are equations which are always true. the `<-effect-` relations express the causal relation between asking for a request to be performed and the response resulting from that request.
On another note, as you can see from the equations, there is no notions of component. Or if you prefer a component is a variable on the leftside of the equations. So for instance screen is a component with two children components.
That is the underlying theory. In practice:
- code does not get written as a list of equations, but through doing some juggling with streams operators. Nothing of the other world really, but there is certainly a ceremony to it, and tips, tricks, gotchas to be aware of.
- Cycle actually does not have a component model stricto sensu. A component model should at least provide a systematic way to combine components into a hierarchy, i.e. a `combine` function by which `parentComponent` = combine(childrenComponents)`. For instance in React, `reactElement = React.createElement(parentComponent, props, childrenComponents)`.
With `cyclejs` you must explicitly write your `combine` function yourself, everytime. That represent probably a portion of the pain you felt.
But conversely look at the other approach. Let's imagine a `ParentComponent` with two child components. To reason about your application, you need to know about the end-to-end sequence of operations involved in all the framework's API. So for instance: `render = render Parent -then-> call onRender of Parent -then-> render Child1 -then-> fetch data -then-> render Child2 -then-> etc.`. This is obviously possible, that is how React works and your own proposed framework too - by the way you do not offer a component model either, stricto sensu). Nothing of the other world really, but there is certainly a ceremony to it, and tips, tricks, gotchas to be aware of (yes I copy pasted from before).
So in short, your evaluation of how bad the ceremony is, the effort it involves etc. will obviously include factors which are specific to you. So I am not doubting your feedback. My whole point here is to say that your mileage may vary and that there is no a priori superiority of one approach over another (stuff like `realizing the appropriate abstraction` as you mention in your post). There are real pain points both ways and what is appropriate to you might be in part the result of a higher familiarity with one approach over another.
If you want to understand what I mean by component model, or want to see what a component model for cyclejs look like, I created my cyclejs component model to address some of the pain points you probably encountered : cf. https://brucou.github.io/posts/a-componentization-framework-...
I am definitely an advocate for reactive programming, as I have used React Native to develop a cross-platform mobile application (https://dedicate.datasilk.io), but when it comes to web development, I am very much an advocate for using the basic tools that were given to us web developers to build web applications.
These tools include mainly HTML & CSS, and when needed, "vanilla" JavaScript (and maybe even a compact jQuery alternative like zepto), but in no way do I advocate for using client-side frameworks for developing web applications. It is because they are difficult to debug, sometimes near impossible, and also because it leads the developer astray from practicing basic concepts such as writing beautifully formatted HTML & CSS or utilizing small bits of JavaScript for event handling & AJAX calls. Instead, they are forced to focus nearly all of their attention to writing JavaScript in what I call a "black box" environment where they send input into the black box and the magical sorcery of said black box somehow outputs a graphical user interface.
A web application should harness the power of the web browser's native capabilities instead of harnessing the powers of a JavaScript framework, because the web is a beautiful place made of beautiful technologies that were built in the golden era of technological creativity. Because of such creativity, many great people came together and invented HTML, CSS, and JavaScript, the near-perfect solution to a very complex, graphically interactive problem.
Sure, you may think that you want to build a single-page application, and that's fine. Why not test your skills and write a micro-library that harnesses the HTML5 History API along with a simple AJAX method to transform your website into that single-page app you fantasize about daily.
Sure, you may think using the JavaScript "import" keyword in all of your many JavaScript files is okay because it's a single-page app that you're building and all those thirty-some-odd JS files will only be loaded one time for each user session when the app initializes, but what you don't understand is that you are lazy and careless and on a daily basis, you make excuses for all of your sins.
You and I both know that your application only needs ONE small JS file on initialization and ONE dynamically loaded JS file for each area within the app that your user may or may not navigate to in the first place.
So, for the love of all humanity, please, please, write HTML & CSS, and pepper it ever-so-lightly with bits of JavaScript (but only when it is absolutely necessary).
If you need a more convincing argument to steer away from client-side JavaScript frameworks, simply read this small comic strip: https://xkcd.com/927/
This is an unnecessarily absolutist position. If HTML, CSS and JavaScript are a "near-perfect solution to a very complex, graphically interactive problem", then why did people come up with front-end frameworks in the first place?
Because they're useful! Frontend developers aren't just cargo culting their architecture decisions; they're trying to finding that the benefits outweigh the downsides, particularly with complex apps and large teams.
This article [1] by Tom Dale (one of the authors of Ember) puts it well:
> I have heard from many developers who have told me that they accepted the argument that vanilla JavaScript or microlibraries would let them write leaner, meaner, faster apps. After a year or two, however, what they found themselves with was a slower, bigger, less documented and unmaintained in-house framework with no community. As apps grow, you tend to need the abstractions that a framework offers. Either you or the community write the code.
Do all web apps need JavaScript frameworks? Of course not! But let's not pretend that apps like Trello don't benefit from them.
I built a trello clone with, you guessed it, vanilla JavaScript (but mostly with HTML & CSS). I built a simple drag drop micro library to drag cards from one list to another. I even built my own custom scroll bar library. Fancy. https://www.github.com/datasilk/kandu
When you take a few hours to build and maintain your own micro-js libraries, you eventually get to a point where you no longer require using 3rd party js frameworks that are bloated and buggy. Why? Because you took the time to learn JavaScript! I would also argue that the only reason why those dev teams realized that they needed abstractions was because they were bad at maintaining & documenting their own micro-libraries. Also, once you build a micro-library for JavaScript, what's the point of maintaining it? Standards are here to stay, no modern web browser will significantly change the way things work, so once you build a JS library that works well, there is no need to maintain it unless you want to add new features or fix bugs.
The level of abstraction that cycle & react & angular and all these frameworks create is unnecessary because again, it leads programmers astray from writing HTML and utilizing JavaScript properly.
My (so-called absolutist) perspective is a coming from a culmination of 18+ years of development time, building VB desktop apps, ASP web app, ASP.NET Web Forms & MVC web app, ASP.NET Core web apps, C# desktop + windows service apps, Magento ecommerce sites, Concrete5 sites, old-school Flash/PHP web apps, a React Native mobile app, and many static websites. I've built a jQuery clone library in under 5kb in size, bootstrap clones with theme support, various drag & drop CMS platforms, and everything in between. I've used failed technologies throughout my career and I've received funding for my projects in the past, I've had to develop sites that support IE 5.5, 6, 7, 8, 9, 10, 11... and from all of my experience, I can say without a doubt that HTML, CSS3, and hand-written JavaScript along with REST APIs is by far the best way to develop a web application, period.
People create front-end frameworks for various reasons. Maybe so they desire fame, maybe because their company had a need for it, maybe because they don't like HTML & "vanilla" JavaScript. Whatever the reason, it doesn't matter. Every framework has its benefits and its drawbacks, and none of them are worth investing time into learning because they all hide behind a layer of JavaScript that doesn't need to exist, period. It's just another thing that every single user will have to download & cache when they visit your site, and if the framework is poorly developed, it'll become another list of things every single user will have to download & cache when they visit your site.
It's really unacceptable, especially to see nearly every major website require visitors to download over 1 MB of unnecessary resources whenever visiting their website. Why? Because of JavaScript bloat.
I’ve been writing single-page apps for about 4 years now, and somewhat agree with your sentiment.
Lately I’ve been experimenting with Jekyll and Vue. The approach is not perfect but it’s a lot closer to the simplicity of the old-school approach you are preaching.
Vue is very close to a straight-forward approach. I would argue that Mustache.js would be a more straight-forward approach than Vue since it is simply a string parsing library for binding JSON objects to templates. No event binding, no black box, just simple template rendering and then you can do whatever you want with the rendered string.
Yeah, but a simple templating system is tedious when you need interactivity or certain types of DOM manipulation based on data.
For example, right now I'm working on a form with dropdowns that filter the next dropdown and with Vue I can do that with very concise code which is simple and easy to reason about.
With Mustache or jQuery it would be tedious. I'd need functions for every little change of the DOM and multiple event listeners and its handlers.
Data-binding is great for this kind of data driven stuff. Not so great for other use cases. Last week I was working on a drag and drop solution with React and it was a pain to figure out how to do it.
Central to the understanding of cycle.js is the concept of observables. An observable is powerful abstraction, as it allows event streams to be programmable. However, an overreached abstraction can be even worse than a lack of abstraction. Cycle.js gratuitously applies the observable abstraction to its component model.
The cycle.js notion of a component is a object that takes multiple observable sources, and emits multiple observable sinks. I have to admit, at first I didn't see this as problematic: to the contrary - it actually struck me as elegant. What I didn't realize, before building a real-world project with cycle, was that this meant all interactions between components were asynchronous. So each component cannot even access the state of another component without communication via observables. This introduces enormous needless complexity into an application. Not only do you have to pay an up front mandatory boilerplate tax to minimally wire together your components, you'll tie yourself in knots readjusting that wiring to get those components to communicate with each other in the way you need.
We actually ended up re-writing the project with our own framework, pickle: https://github.com/pickle-ts/pickle For us, this substantially increased maintainability / reduced the code size of our application compared with cycle.js, or the prototype we wrote with react/redux. The code reduction came from realizing the appropriate abstraction: the entire application should be composed of an object-oriented hierarchy of stateful components, where on each update, these mutable components render themselves as immutable v-dom trees. Anyway, to counteract any pluggishness (our framework is obviously super new so is only of interest to ultra early-adopters), the conservative sensible choice for a web framework right now is to use react or vue /w a state manager. Not cycle.js.