I'm not sure that's true in general.
For example, for some tools there's no single 'one level abstraction below'. Let's take regular expressions as a simple example. You can use them effectively, no matter whether your regular expression matcher uses NFAs or Brzozowski derivatives under the hood as the 'one level [of] abstraction below'.
(Just be careful, if your regular expression matcher uses backtracking, you might get pathological behaviour. Though memoisation makes that less likely to hit by accident in practice.)
If you're OK with the ocassional catastrophically slow regex: https://swtch.com/~rsc/regexp/regexp1.html , sure.
But you do need to understand the abstraction of strings, code points and so on, if you want to do regexes on unicode that doesn't stop at the ASCII level.
In general yes: it's not an absolute law that you need to "know one layer below". You can code in Python and never know about machine code.
But knowing the layers below sure does help making better decisions if you want e.g. to optimize this Python code. Knowing how the code will be executed, memory layouts, how computers work, access latencies of memory, disk, network, etc sure does help.
I already addressed that in my original comment. Approaches based NFAs and Brzozowski derivatives don't have these flaws; but you don't need to know anything about how they work to use them.
You just need to read one blog post that tells you to avoid regular expression matchers that use backtracking, and you are good to go. You don't even need to understand why matching via backtracking is bad.
> But you do need to understand the abstraction of strings, code points and so on, if you want to do regexes on unicode that doesn't stop at the ASCII level.
Why?
Yeah, no.
You might not be able to avoid using your standard lib's regex, or your project's chosen regex dependency - based on team/company policy. So it's not as simple as "use a regex engine that doesn't has this flaw".
Then if you want to avoid the cost, you need to know what backtracking is, to the level of understanding which kind of expressions can give you those performance issues.
>Why?
Because there are tons of factors that can affect your regex experience with unicode, normalization, different lower/upper case treatment, composite characters that don't match even though it looks like you typed the same character in your query, handling new unicode characters (ASCII 7/8 bit has been fixed for decades) and so on.
Well, yes, if someone forces you to use tools that have flaws, you need to learn about the flaws so you can work around them. Like when using a shoe as a hammer.
I'm not sure that proves anything about abstractions?
See also https://blog.codinghorror.com/the-php-singularity/
> Because there are tons of factors that can affect your regex experience with unicode, normalization, different lower/upper case treatment, composite characters that don't match even though it looks like you typed the same character in your query, handling new unicode characters (ASCII 7/8 bit has been fixed for decades) and so on.
Thanks.
A better example might be that of a compiler, where you (very rarely) need to look at the asm output or encounter a case where the compiler generates incorrect code and you need to debug why.
Nope, that will never happen to me. No regular expression has that behaviour.
There are some bad implementations of regular expression matching that have these problems for some regular expressions. But you can avoid those bad implementations without understanding anything.
> I encountered one a few months ago, so it’s not at all uncommon or difficult to encounter.
That only happens, if you use a regular expression matcher that uses backtracking. Sane regular expression matchers take linear time on all input strings.
> If you’re curious enough, you’d end up reading about how regexes work under the hood.
There's no one single way regular expressions work under the hood. You had the misfortune of using a matcher that uses backtracking and is prone to catastrophic exponential runtimes.
There's multiple different ways to implement regular expression matchers. But a user doesn't have to care or understand anything (they just need to avoid the buggy ones).
Sure, if you read up on how regular expressions work under the hood, you can learn to avoid those bad matchers. (Or you can learn how to live with your batch implementation, if you are feeling masochistic.)
But that's entirely optional: You only need to read one blog post that tells you to use grep and avoid eg Perl. You don't need to understand why backtracking is bad for regular expression matching; as long as you avoid those bad matchers.
Regular expression libraries that don't use backtracking are available for many languages. Yes, some languages have bad libraries that use backtracking, too. But bad libraries will always exist, they aren't an excuse when there are more good libraries around then ever before.
In fact it wasn't necessary for me to qualify that with "I would say". I do say, and it simply does. You've made exactly no argument this entire thread.
Maybe everyone doesn't need to know everything, but the skin of stuff anyone needs to know is thicker than 0, and has no absolute boundary layer either, you just generally need to know less the further from your own work. But that never goes all the way to 0. You have to rely on other people to have done their jobs, but you still at least have to understand what those jobs are, that they exist and how you ultimately interact with them and how they impact you.
You said so yourself several times which makes this all farcical.
Doesn’t that exactly demonstrate why using the tool effectively requires an understanding of the implementation (or possible implementations) behind the abstraction?
You can just consult a list that tells you which regular expression matchers to avoid (like eg Perl), and which ones are good (like grep), and you are good to go. No need to understand anything.
You can just consult a list that tells you which regular expression matchers to avoid (like eg Perl), and which ones are good (like grep), and you are good to go. No need to understand anything.
Unless you're coding in Perl or sed or whatever then regular expressions arent really a primary abstraction. And even when they are, I don't see how the implementation wouldn be accessible as a lower level. They're not really a layered abstraction.
The only thing you need to watch out for is regular expression matchers that are prone to exponential blowup. But you don't need to understand anything; you can just consult a list of which ones are bad and which ones are good, and then avoid the bad ones; without any understanding of the mechanics necessary.
> You have a point, but it seems clear to me that your parent commenter was referring to more pervasive abstractions like frameworks and probably programming languages.
Probably. I was just looking for the simplest example that the maximum number of people would be familiar with.
Also, the poster says this line:
> Finding a dev who actually groks the gap between useEffect and useMemo, error boundaries, hook based fetching or my (un)favourite, authentication, is difficult.
But truthfully the problems those constructs solve are problems in every paradigm. If you can’t grasp the difference these things despite being a heavy user of a library, you may… want to spend some time reading the docs at some point.
The "UI as a function of state" was not just sold as "more functional/simpler", but also as a necessity to have a DOM diffing algorithm to minimize updates and be "faster".
The virtual DOM is not faster than performing direct mutations of the actual DOM, the virtual DOM is a tool that allows the normally slow approach of "blow away and rebuild the world" to be fast enough to put into use.
Prior to React, performant apps that weren't using frameworks that regenerated the entire DOM tree would simply manually mutate the DOM nodes based on what it was doing. If your app was loading a new comment, it would find the list of comment nodes and append a new one to it, same as a virtual DOM would.
The problem is that this could be error-prone in large apps—are you even sure that the comment list is still on the page? If it's not, do you need to add it to the page or is this a completely new view and you're reacting to a network request that finished after the user navigated away from the page? That's the kind of finnicky manual work that React helped simplify, but the performance was the same as an app manually mutating the DOM plus the extra virtual DOM comparisons.
https://www.quirksmode.org/dom/innerhtml.html
https://segdeha.com/experiments/innerhtml/
The thing which was slow is changing more than you need to change. When the React marketing push included performance, that’s almost always what they were comparing it to - someone would have slow code making the same updates multiple times or triggering reflows by forcing the browser to do a partial update to measure something, and then updating again.
There’s an argument that the guaranteed overhead of using React is better because it helps the average developer who doesn’t measure performance much do a better job than they might have otherwise, and that can be true for many people but it also tended to get oversimplified to “React is fast” despite widespread evidence to the contrary.
But in a comment thread of people saying the virtual DOM is not faster than equivalent manual DOM mutations you can understand why "The virtual DOM was faster than the DOM" got understood differently since it lacked that clarification.
Not really a misconception: it was explicitly pushed and advertised as such, most of the advocacy (especially from React fans) at the time React was introduced and started gaining traction was about the virtual DOM being "faster".
>React changed things so that the state of the DOM is always derived from your own app's state, which eliminates what turned out to be a pretty tedious and error-prone amount of work in complex web apps.
I dunno, I used Redux for a few years, and it was a "pretty tedious and error-prone amount of work in complex web apps" too.
I'm sure React fans who misunderstood the virtual DOM (which was explicitly advertised because it was the most popular part of React[1] vs the other parts that people were more critical of) may have said this, but I never saw the project itself advertising itself as faster than vanilla JS, only that it could be faster than competing frameworks, _especially_ ones that rebuilt the DOM unnecessarily.
> I dunno, I used Redux for a few years
Redux is a different library than React. State management across an app is a problem even for non-React applications, it's not like React _uniquely requires_ work to address state management.
I don't see why your dissatisfaction with Redux has anything to do with React making a very specific and buggy part of building web apps easier to manage in return for a performance cost and following its API.
[1]: See mention near the end of https://medium.com/@dan_abramov/youre-missing-the-point-of-r...
https://svelte.dev/blog/virtual-dom-is-pure-overhead#how-did...
Here’s @vjeux: “React is a JavaScript library for building user interfaces developed by Facebook. It has been designed from the ground up with performance in mind.”
https://calendar.perfplanet.com/2013/diff/
“Optimized for performance and memory footprint”
http://slid.es/johnlynch/reactjs
If you worked on the web a decade ago, this stuff was everywhere and because Facebook was at its peak there was this bizarre reality-denial effect where people would have an app which was slow, soaked memory like a sponge, and they’d insist that was as good as it could be because Facebook and Instagram were using the same framework. Reality intruded enough eventually that people started talking more about things like developer experience but you can still find people arguing that needing 4MB of JavaScript for a contact form is normal.
I guess you could say that not being specific was misleading but we'd have to agree to disagree on that. I _did_ work on the web a decade ago and my recollection is that it wasn't ever unclear that React's performance claims were that it was fast for a framework and not faster than equivalent manual DOM mutations.
That was a very common claim - if you search for old comments there was some very magical thinking about virtual DOMs at the time. I do agree it was worse in the fanboy circles - there’s a game of telephone where nuance is lost at every iteration so you’d sometimes have a blog post where someone said they saw a big win replacing a thicket of jQuery plugins and it’d eventually be repeated as “React is ZOMG FAST!!!!” with a few people trying in vain to suggest that replacing a bunch of organic code with something planned probably mattered more.
Anyway, my point isn’t that React is terrible but more that you really want to measure & review assumptions to make sure you’re not relying on something stale. It’s still easy to find assumptions from the IE6 era shaping what people think of as fast or slow, and especially with the massive front end framework trend it’s easy for people to have enough layers that their understanding of their application’s performance is far off. Choosing React is fine, just make the trade offs deliberately.
Even small Redux apps can start off with a lot of indirection as soon as they extract logic into selectors. This is probably the first area where Redux becomes more complex that what you were doing before:
https://redux.js.org/usage/deriving-data-selectors
It reminds me of reading a bunch of parser combinators: it feels like you're reading code that builds something that does what you want instead of the code that just does what you want.
const makeSelectItemsByCategory = () => {
const selectItemsByCategory = createSelector(
[state => state.items, (state, category) => category],
(items, category) => items.filter(item => item.category === category)
)
return selectItemsByCategory
}At the time it was. Especially compared to how apps were built at the time.
Note that even today virtual DOM libs (not the same implementation as React's) are still among the fastest: https://krausest.github.io/js-framework-benchmark/2024/table...
This makes no sense. Redux is extremely simple with a very small API. If the code using it is tedious and error-prone, that's the fault of the people who wrote that code not Redux.
E.g. "everything must be an action and have an associated reducer" is nothing to do with Redux itself.
Redux splits things up that belong together. Redux makes you write more code. Of course it's more complex.
Even it's creator advised that you probably don't need it.
This is my whole point. Redux in itself is not complex. The elaborate flows people create with it are complex.
It's not Redux's fault that you're using it when you shouldn't be.
Redux is concept overload. It's abstractions on top of abstractions on top of abstractions...
It makes simple things complex.
This is a very common misconception. React was meant to provide one-way data flow. With an app built directly on the DOM you have to deal with the DOM having its own state to manage on top of the state your own code is storing. You have a list of comments in a variable, and a list of DOM elements for each comment, and nothing but your own code is making sure they stay in sync with each other. React changed things so that the state of the DOM is always derived from your own app's state, which eliminates what turned out to be a pretty tedious and error-prone amount of work in complex web apps.
It wasn't the first to provide this but its API was unique and compelling vs existing alternatives. The virtual DOM, server-side rendering, etc. are not things that make React faster than non-React code, they are things that counteract the inherent slowness in React's design to make it competitive with non-React code.
Watch the original JSConf talk where React was publicly announced and released, and notice how the talk is mostly about the one-way data binding, and the virtual DOM/reconciliation are mentioned in the back half as ways React catches up to non-React app speeds: https://www.youtube.com/watch?v=GW0rj4sNH2w