Hacker News new | ask | show | jobs
Widget Driven Development (alexei.me)
40 points by alexey2020 1655 days ago
8 comments

> [Widget Driven Development] makes the data flows transparent, architecture flexible, the code resilient and easy to test.

A direct quote from my "expert-junior-evangelical" phase. When I marveled at my own cleverness & marketed my brand-new paradigms to my unfortunate peers. Lots of assertions, few citations, no research. All the fun was in coding, not in studying books or even the industry at large. Then, business requirements changed and I was left to maintain over-engineered monolith of my own making.

After a few cycles of hubris, I learned the danger of selling fads as evolutions. It hollows you out.

> not in studying books or even the industry at large

In fairness the author spends most of the article explaining the approach of the industry at large (it's a good explanation too).

It appears like a good explanation. But other commenters have pointed out how it lacks precision. Name dropping CSS modules, React query and other technologies is marketing, not research.

By comparison, this article properly analyzes an actual industry trend, not generalizing one developers experience: https://martinfowler.com/articles/micro-frontends.html

I’m not a believer in micro-frontends but the quality and intent Marin fowler’s of writing is actually respectable.

>the quality and intent Marin fowler’s of writing is actually respectable.

Sorry, didn't want to be picky, but the article you mentioned is not written by MarTin Fowler. And I also didn't find there many "citations" you were looking for in my article.

I'm sorry that you found my article of a little quality.

I did my best trying to analyze different approaches to Data Management, How we came to those and their problems. I illustrated those with the my own diagrams to help readers better understand the concepts.

The article is based on my 10+ years experience in the industry.

It went through many iterations of reviews and corrections.

There nothing unique in the approach I described. It builds on what libraries like ReactQuery allows to do.

I basically just tried to formalise why I see this approach as the next logical step in how we approach building UI apps.

I'm not a native English speaker and not a professional blogger. Most probably there are ways to write such an article better. I do my best learning how write better.

Having said that, I think you are not fair comparing it with your "expert-junior-evangelical" phase.

Thank you for formalising this.

We came to a similar conclusion as we moved from imperatively handling server state to using ReactQuery.

What I got to like the most was the small cognitive load. Other than the minimally shared UI state, everything I need to hold in working memory is close by. Not that it's novel or impossible to achieve the same through other means, but that this way of building UIs often leads to a manageable result.

It works very well in cases where the server is the source of truth, but I don't want it to feel like it is.

The creator of ReactQuery talks extensively about the philosophy behind his library in a recent PodRocket episode. [0]

0 - https://podrocket.logrocket.com/tanstack

"the small cognitive load" - true that!

Thanks for the link to the podcast. Will definitely check this out

This article gives a great rundown of various UI layer approaches, the problems with each, and how the subsequent ones solved them. I hadn’t seen react-query or its listed alternatives before, so thanks!

The one thing I’m not clear on is how this is much different from the “single global store” approach. It looks like that’s what this approach uses, and the main difference is in the “store API.” In other words, usage from components is nearly identical, and it’s the mechanics of the store itself that is changed. There’s a layer of focused queries between the store and components that hides the access (and maybe update?) mechanics from the widgets. So instead of

  getFromStore(“books”, bookId) // pseudocode, I am not a Redux user
you instead use

  useBookQuery(bookId) // which includes some nice things like { isLoading, error }
It doesn’t seem to resolve the need to handle the following bullet point found in the store drawbacks, it “just” pushes it into a group of queries (and presumably updates).

> we now have to deal with a completely new concept, the Store, and care about a bunch of new things, such as designing and maintaining Store structure, appropriately updating data in the Store, data normalization, […]

Is this separation the point? Or am I missing something else? Keeping the store access mechanics separated from the widgets that use them makes sense to me, especially when it has built-in loading/error notifications. I was going to say that “widget driven development” doesn’t really point to what this achieves by making that separation, but I may be coming around to the term with those other considerations.

I suggest emphasizing the loading/error mechanics more strongly at the end of the article. React is doing a lot of work with Suspense to improve their ability to give UI feedback in those states, and it looks like this approach simplifies the data side of that.

Thank you for the valuable feedback!

Very good question about the difference between `getFromStore` and `useBookQuery`.

When using `getFromStore` you have the expectation that the value is already in the Store. Someone should have already put the value there somehow.

You have also the expectation that `getFromStore` is synchronous and simply gives you nothing if the value is not there.

With `useBookQuery` you expect the value to be fetched from Server. There is no 3rd party to care about putting the value in the Store. `useBookQuery` is simply like `fetch('...')` from a Component perspective.

With libs like ReactQuery every component "simply" fetches what it needs, sort of directly communicating to the Backend. No intermediary party (like a Store)

>React is doing a lot of work with Suspense ...

Yeah, I heard about React team collaboration with libs like ReactQuery on making Suspense work with those, also on server side. But I'm not much into that Suspense topic, so decided to not mention anything about it.

>I suggest emphasizing the loading/error mechanics more strongly at the end of the article.

Thanks for the suggestion. I will think about it.

> With libs like ReactQuery every component "simply" fetches what it needs, sort of directly communicating to the Backend. No intermediary party (like a Store)

But in the article, you wrote:

> What if we could have an intermediate player between our components and Backend, say an API wrapper or interceptor, solving all those problems under the hood: [deduping, consistency, invalidation/automatic data refresh, hiding implementation]

The way I look at it, I don’t expect (or want) the value to be fetched from the server, I expect it to be fetched from “the single source of truth that abstracts the server away from my widget’s concerns.” I still think of that as a store, because that’s what stores advertise themselves as, it’s just one with a better API. The differences in how we are talking about this may come down to semantics—or maybe you can trust your servers more than I trust mine :)

To me, “directly communicating with the backend” implies that I have to have REST calls in my components, and handle all that comes with that, and that makes me want to run and hide. I don’t think that is your intent. There is still an intermediary here, and it is one that better separates ALL of the data fetching aspects from the widget by being async and providing good feedback on request/response status. Your diagram with the “API Wrapper” in it displays this quite well.

It’s a good design principle; it’s how I want to build things! At work we recently migrated to React, and I tried to follow similar principles without awareness of things like ReactQuery. I will definitely be looking more into this (especially the update side of things), and thinking about if/how it could simplify what we are doing. I’m not sure if it will work for us, but maybe I can at least steal a few more good ideas.

Thanks for writing the article and answering my question!

>But in the article, you wrote:

I also wrote a bit below: "be transparent to components and not affect their logic in any way (make components think they communicate to Backend directly)"

I met people trying to use ReactQuery with the mindset that it's a Store. The result was that they were greatly frustrated with the outcome. That mindset led them to use ReactQuery in a way it's not supposed to be used. Every now and then they wanted to directly manipulate with the Cache (the "Store" underneath ReactQuery).

That's why I find that Store-mindset very dangerous when working with such Libraries.

Better come with a mindset that there is no Store at all. Better think that "useBookQuery" is just a simple hook to fetch data. NO STORE.

>To me, “directly communicating with the backend” implies that I have to have REST calls in my components, and handle all that comes with that

This is exactly what I meant :)

I'm glad that you found the article useful. Thanks again for providing your feedback!

Thanks again for your thoughtful response.

I have more questions about whether ReactQuery can help me out when working with a bad legacy API, but I don’t know how to ask them clearly. It would be unfair to ask you to understand the system I’m working with, especially when you’ve already written an article that clearly describes the idea and have been so generous with your responses. I will be looking at this more deeply for sure.

This kind of thing is why I come to HN.

The benefits seem unclear to me, especially how this solves the shared-state issue. Is it just that each "widget" calls the same API "manager" and propagates the changes to every widget in the event of a change?

Isn't this exactly what Redux already do if you use actions to fetch data and put it into the store, with the difference that each "widget" manage filters and state based on the fetched API data? Seems like it could create a lot more code with less re-usage, since every widget has to manage this themselves.

With pure Redux approach the mutation flow is more complex: - send mutation request to Backend - fetch updated data - put the updated data in the Store - see the updated data propagated in components

The suggested approach: - send mutation request to Backend - see the updated data being fetched and displayed by components

To me the second is much cleaner and easier to reason about

Wouldn't the lack of a shared store on the client lead to an inconsistent application state when different components fetch different versions of the data? I.e. one part of the UI could think you're logged in as admin while another thinks you're a guest user.
>Seems like it could create a lot more code with less re-usage, since every widget has to manage this themselves.

It might create a bit more code in components, agree. I think this is a fair price for the all benefits it brings.

That said, the total amount of code might be even less (no action-creators, reducers, etc.).

Also declarative nature of the libraries makes the added code very easy to follow.

The key thing to realise with widgets is that while the widget code is indeed complex, it’s reusable and generally you’re not writing it.
One problem is the idea that there is a one-size-fits-all solution. An application should be structured in such a way that it fits the problem that you are trying to solve as ergonomically as possible. 'Widget Driven Development' might be very good in some circumstances but not in others.
Agree. There are applications where widgets approach would not bring much benefits.

Let say, applications with lots of UI state (state that doesn't exist on Server).

Also not sure how it would work with realtime apps.

It's the same with every approach, the are always exceptions from the rule.

That said, I see the benefit of this approach for the majority of UI apps, which heavily rely on Server data.

This is rather vague.

Let's take a more concrete example:

Let's say you want to add a button somewhere that hides/shows another widget. Surely you don't want to go to the backend to do that? So how do you do it? With the store approach, clicking the button creates an action, that modifies the store (changing a visibility parameter somewhere) and repaints the UI.

In the diagram there is no flow back from the "API" box to the widgets. Though it still something that needs to happen.

The "API" necessarily contains some state, at least to track which component needs which data.

>Let's say you want to add a button somewhere that hides/shows another widget.

That's a good case. This is purely UI state, right? (we don't store it on the server).

For UI state we still need to depend on prop-drilling or State Management solutions.

In the article I'm mainly talking about the Data which exist on Backend (as you can also see from all the pictures). In my experience such Server data is 90-95% of all data in most UI applications and that data contributes the most to the complexity.

Pure UI state is often just a fraction of the the whole State and is's often synchronous, so managing it should not be complex. So this kind of coupling will still exist.

To clarify, I do not say Widgets should be 100% independent. We do not achieve complete decoupling. But moving Server Data under control of Widgets gives us closer to this.

Well, you're comparing things that are not quite comparable then...

The store approach is all about how the app handles local UI changes. Whether the action goes and fetches data from the server doesn't matter. What matters is that the store gives you a single source of truth that represents the state of the UI, and that this state gets one-way-synced with its actual dom representation.

>Whether the action goes and fetches data from the server doesn't matter

I tried to explain in the article why it matters.

If you are comfortable with putting all the data in a single Store and this works fine to you, then you may disregard the article.

Would this work with server side rendered applications? We SSR a lot of the data calls, as the identifiers to be fetched is usually available in the URL.

——

Nvm, I see that’s listed as a React Query feature.

And yet another round on the Web GUI carrousel.

https://en.wikipedia.org/wiki/List_of_widget_toolkits