Status quo
This commit is contained in:
@@ -3,6 +3,14 @@ title: "First, It Must Work"
|
|||||||
date: 2023-05-11T23:00:26-04:00
|
date: 2023-05-11T23:00:26-04:00
|
||||||
draft: false
|
draft: false
|
||||||
references:
|
references:
|
||||||
|
- title: "Second-guessing the modern web - macwright.com"
|
||||||
|
url: https://macwright.com/2020/05/10/spa-fatigue.html
|
||||||
|
date: 2023-07-04T01:51:17Z
|
||||||
|
file: macwright-com-vvkegs.txt
|
||||||
|
- title: "In defense of the modern web - DEV Community"
|
||||||
|
url: https://dev.to/richharris/in-defense-of-the-modern-web-2nia
|
||||||
|
date: 2023-07-04T01:51:24Z
|
||||||
|
file: dev-to-ptnb0b.txt
|
||||||
- title: "The Grug Brained Developer"
|
- title: "The Grug Brained Developer"
|
||||||
url: https://grugbrain.dev/
|
url: https://grugbrain.dev/
|
||||||
date: 2023-05-12T13:08:33Z
|
date: 2023-05-12T13:08:33Z
|
||||||
@@ -30,17 +38,22 @@ references:
|
|||||||
* So much stuff doesn't work -- bugs stop you from accomplishing your desired task
|
* So much stuff doesn't work -- bugs stop you from accomplishing your desired task
|
||||||
* Over time, complexity creeps in, making changes gets harder
|
* Over time, complexity creeps in, making changes gets harder
|
||||||
* The focus on performance, serverless, microservices, etc. -- these increase complexity, reduce reliability
|
* The focus on performance, serverless, microservices, etc. -- these increase complexity, reduce reliability
|
||||||
|
* Everyone agrees that the current status quo sucks, but we disagree on the solution: go back to how things worked in the past, or continue down the path we're on with the hope that things will improve
|
||||||
|
* Reference ["Second-guessing the modern web"][1] and ["In defense of the modern web"][2]
|
||||||
|
|
||||||
|
[1]: https://macwright.com/2020/05/10/spa-fatigue.html
|
||||||
|
[2]: https://dev.to/richharris/in-defense-of-the-modern-web-2nia
|
||||||
|
|
||||||
### Links
|
### Links
|
||||||
|
|
||||||
* [The Grug Brained Developer][1]
|
* [The Grug Brained Developer][3]
|
||||||
* [Even Amazon can't make sense of serverless or microservices][2]
|
* [Even Amazon can't make sense of serverless or microservices][4]
|
||||||
* [Imaginary Problems Are the Root of Bad Software][3]
|
* [Imaginary Problems Are the Root of Bad Software][5]
|
||||||
* [When to Build Millennia Sewers][4]
|
* [When to Build Millennia Sewers][6]
|
||||||
* [We are wasting up to 20% of our time on computer problems, says study][5]
|
* [We are wasting up to 20% of our time on computer problems, says study][7]
|
||||||
|
|
||||||
[1]: https://grugbrain.dev/
|
[3]: https://grugbrain.dev/
|
||||||
[2]: https://world.hey.com/dhh/even-amazon-can-t-make-sense-of-serverless-or-microservices-59625580
|
[4]: https://world.hey.com/dhh/even-amazon-can-t-make-sense-of-serverless-or-microservices-59625580
|
||||||
[3]: https://cerebralab.com/Imaginary_Problems_Are_the_Root_of_Bad_Software
|
[5]: https://cerebralab.com/Imaginary_Problems_Are_the_Root_of_Bad_Software
|
||||||
[4]: https://taylor.town/millennium-sewer
|
[6]: https://taylor.town/millennium-sewer
|
||||||
[5]: https://techxplore.com/news/2023-06-problems.html
|
[7]: https://techxplore.com/news/2023-06-problems.html
|
||||||
|
|||||||
2595
static/archive/dev-to-ptnb0b.txt
Normal file
2595
static/archive/dev-to-ptnb0b.txt
Normal file
File diff suppressed because it is too large
Load Diff
284
static/archive/macwright-com-vvkegs.txt
Normal file
284
static/archive/macwright-com-vvkegs.txt
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
#[1]macwright.com [2]macwright.com
|
||||||
|
|
||||||
|
Tom MacWright
|
||||||
|
|
||||||
|
tom@macwright.com
|
||||||
|
|
||||||
|
Tom MacWright
|
||||||
|
|
||||||
|
* [3]Writingâ‡
|
||||||
|
* [4]Reading
|
||||||
|
* [5]Photos
|
||||||
|
* [6]Projects
|
||||||
|
* [7]Drawings
|
||||||
|
* [8]About
|
||||||
|
|
||||||
|
Second-guessing the modern web
|
||||||
|
|
||||||
|
The emerging norm for web development is to build a React single-page
|
||||||
|
application, with server rendering. The two key elements of this
|
||||||
|
architecture are something like:
|
||||||
|
1. The main UI is built & updated in JavaScript using React or
|
||||||
|
something similar.
|
||||||
|
2. The backend is an API that that application makes requests against.
|
||||||
|
|
||||||
|
This idea has really swept the internet. It started with a few major
|
||||||
|
popular websites and has crept into corners like marketing sites and
|
||||||
|
blogs.
|
||||||
|
|
||||||
|
I’m increasingly skeptical of it.
|
||||||
|
|
||||||
|
There is a sweet spot of React: in moderately interactive interfaces.
|
||||||
|
Complex forms that require immediate feedback, UIs that need to move
|
||||||
|
around and react instantly. That’s where it excels. I helped build the
|
||||||
|
editors in [9]Mapbox Studio and [10]Observable and for the most part,
|
||||||
|
React was a great choice.
|
||||||
|
|
||||||
|
But there’s a lot on either side of that sweet spot.
|
||||||
|
|
||||||
|
The high performance parts aren’t React. [11]Mapbox GL, for example, is
|
||||||
|
vanilla JavaScript and probably should be forever. The level of
|
||||||
|
abstraction that React works on is too high, and the cost of using
|
||||||
|
React - in payload, parse time, and so on - is too much for any company
|
||||||
|
to include it as part of an SDK. Same with the [12]Observable runtime,
|
||||||
|
the juicy center of that product: it’s very performance-intensive and
|
||||||
|
would barely benefit from a port.
|
||||||
|
|
||||||
|
The less interactive parts don’t benefit much from React. Listing
|
||||||
|
pages, static pages, blogs - these things are increasingly built in
|
||||||
|
React, but the benefits they accrue are extremely narrow. A lot of the
|
||||||
|
optimizations we’re deploying to speed up these things, things like
|
||||||
|
bundle splitting, server-side rendering, and prerendering, are
|
||||||
|
triangulating what we had before the rise of React.
|
||||||
|
|
||||||
|
And they’re kind of messy optimizations. Here are some examples.
|
||||||
|
|
||||||
|
Bundle splitting.
|
||||||
|
|
||||||
|
As your React application grows, the application bundle grows. Unlike
|
||||||
|
with a traditional multi-page app, that growth affects every visitor:
|
||||||
|
you download the whole app the first time that you visit it. At some
|
||||||
|
point, this becomes a real problem. Someone who lands on the About page
|
||||||
|
is also downloading 20 other pages in the same application bundle.
|
||||||
|
Bundle splitting ‘solves’ this problem by creating many JavaScript
|
||||||
|
bundles that can lazily load each other. So you load the About page and
|
||||||
|
what your browser downloads is an ‘index’ bundle, and then that ‘index’
|
||||||
|
bundle loads the ‘about page’ bundle.
|
||||||
|
|
||||||
|
This sort of solves the problem, but it’s not great. Most bundle
|
||||||
|
splitting techniques require you to load that ‘index bundle’, and then
|
||||||
|
only once that JavaScript is loaded and executed does your browser know
|
||||||
|
which ‘page bundle’ it needs. So you need two round-trips to start
|
||||||
|
rendering.
|
||||||
|
|
||||||
|
And then there’s the question of updating code-split bundles. User
|
||||||
|
sessions are surprisingly long: someone might have your website open in
|
||||||
|
a tab for weeks at a time. I’ve seen it happen. So if they open the
|
||||||
|
‘about page’, keep the tab open for a week, and then request the ‘home
|
||||||
|
page’, then the home page that they request is dictated by the index
|
||||||
|
bundle that they downloaded last week. This is a deeply weird and
|
||||||
|
under-discussed situation. There are essentially two solutions to it:
|
||||||
|
1. You keep all generated JavaScript around, forever, and people will
|
||||||
|
see the version of the site that was live at the time of their
|
||||||
|
first page request.
|
||||||
|
2. You create a system that alerts users when you’ve deployed a new
|
||||||
|
version of the site, and prompt them to reload.
|
||||||
|
|
||||||
|
The first solution has a drawback that might not be immediately
|
||||||
|
obvious. In those intervening weeks between loading the site and
|
||||||
|
clicking a link, you might’ve deployed a new API version. So the user
|
||||||
|
will be using an old version of your JavaScript frontend with a new
|
||||||
|
version of your API backend, and they’ll trigger errors that none of
|
||||||
|
your testing knows about, because you’ll usually be testing current
|
||||||
|
versions of each.
|
||||||
|
|
||||||
|
And the second solution, while it works (and is what we implemented for
|
||||||
|
Mapbox Studio), is a bizarre way for a web application to behave.
|
||||||
|
Prompting users to ‘update’ is something from the bad old days of
|
||||||
|
desktop software, not from the shiny new days of the web.
|
||||||
|
|
||||||
|
Sure: traditional non-SPA websites are not immune to this pitfall.
|
||||||
|
Someone might load your website, have a form open for many weeks, and
|
||||||
|
then submit it after their session expired or the API changed. But
|
||||||
|
that’s a much more limited exposure to failure than in the SPA case.
|
||||||
|
|
||||||
|
Server-Side Rendering
|
||||||
|
|
||||||
|
Okay, so the theory here is that SPAs are initially a blank page, which
|
||||||
|
is then filled out by React & JavaScript. That’s bad for performance:
|
||||||
|
HTML pages don’t need to be blank initially. So, Server-Side Rendering
|
||||||
|
runs your JavaScript frontend code on the backend, creating a
|
||||||
|
filled-out HTML page. The user loads the page, which now has
|
||||||
|
pre-rendered content, and then the JavaScript loads and makes the page
|
||||||
|
interactive.
|
||||||
|
|
||||||
|
A great optimization, but again, caveats.
|
||||||
|
|
||||||
|
The first is that the page you initially render is dead: you’ve created
|
||||||
|
the [13]Time To Interactive metric. It’s your startup’s homepage, and
|
||||||
|
it has a “Sign upâ€<C3A2> button, but until the JavaScript loads, that button
|
||||||
|
doesn’t do anything. So you need to compensate. Either you omit some
|
||||||
|
interactive elements on load, or you try really hard to make sure that
|
||||||
|
the JavaScript loads faster than users will click, or you make some
|
||||||
|
elements not require JavaScript to work - like making them normal links
|
||||||
|
or forms. Or some combination of those.
|
||||||
|
|
||||||
|
And then there’s the authentication story. If you do SSR on any pages
|
||||||
|
that are custom to the user, then you need to forward any cookies or
|
||||||
|
authentication-relevant information to your API backend and make sure
|
||||||
|
that you never cache the server-rendered result. Your
|
||||||
|
formerly-lightweight application server is now doing quite a bit of
|
||||||
|
labor, running React & making API requests in order to do this
|
||||||
|
pre-rendering.
|
||||||
|
|
||||||
|
APIs
|
||||||
|
|
||||||
|
The dream of APIs is that you have generic, flexible endpoints upon
|
||||||
|
which you can build any web application. That idea breaks down pretty
|
||||||
|
fast.
|
||||||
|
|
||||||
|
Most interactive web applications start to triangulate on “one query
|
||||||
|
per page.â€<C3A2> API calls being generic or reusable never seems to persist
|
||||||
|
as a value in infrastructure. This is because a large portion of web
|
||||||
|
applications are, at their core, query & transformation interfaces on
|
||||||
|
top of databases. The hardest performance problems they tend to have
|
||||||
|
are query problems and transfer problems.
|
||||||
|
|
||||||
|
For example: a generically-designed REST API that tries not to mix
|
||||||
|
‘concerns’ will produce a frontend application that has to make lots of
|
||||||
|
requests to display a page. And then a new-age GraphQL application will
|
||||||
|
suffer under the [14]N+1 query problem at the database level until an
|
||||||
|
optimization arrives. And a traditional “make a query and put it on a
|
||||||
|
pageâ€<C3A2> application will just, well, try to write some good queries.
|
||||||
|
|
||||||
|
None of these solutions are silver bullets: I’ve worked with
|
||||||
|
overly-strict REST APIs, optimization-hungry GraphQL APIs, and
|
||||||
|
hand-crafted SQL APIs. But no option really lets a web app be careless
|
||||||
|
about its data-fetching layer. Web applications can’t sit on top of
|
||||||
|
independently-designed APIs: to have a chance at performance, the
|
||||||
|
application and its datasource need to be designed as one.
|
||||||
|
|
||||||
|
Data fetching
|
||||||
|
|
||||||
|
Speaking of data fetching. It’s really important and really bizarre in
|
||||||
|
React land. Years ago, I expected that some good patterns would emerge.
|
||||||
|
Frankly, they didn’t.
|
||||||
|
|
||||||
|
There are decent patterns in the form of GraphQL, but for a React
|
||||||
|
component that loads data with fetch from an API, the solutions have
|
||||||
|
only gotten weirder. There’s great documentation for everything else,
|
||||||
|
but old-fashioned data loading is relegated to one example of how to
|
||||||
|
mock out ‘fetch’ for testing, and lots of Medium posts of varying
|
||||||
|
quality.
|
||||||
|
__________________________________________________________________
|
||||||
|
|
||||||
|
Don’t read this as anti-React. I still think React is pretty great, and
|
||||||
|
for a particular set of use cases it’s the best tool you can find. And
|
||||||
|
I explicitly want to say that – from what I’ve seen – most other
|
||||||
|
Single-Page-Application tools share most of these problems. They’re
|
||||||
|
issues with the pattern, not the specific frameworks used to implement
|
||||||
|
it. React alternatives have some great ideas, and they might be better,
|
||||||
|
but they are ultimately really similar.
|
||||||
|
|
||||||
|
But I’m at the point where I look at where the field is and what the
|
||||||
|
alternative patterns are – taking a second look at unloved, unpopular,
|
||||||
|
uncool things like Django, Rails, Laravel – and think what the heck is
|
||||||
|
happening. We’re layering optimizations upon optimizations in order to
|
||||||
|
get the SPA-like pattern to fit every use case, and I’m not sure that
|
||||||
|
it is, well, worth it.
|
||||||
|
|
||||||
|
And it should be easy to do a good job.
|
||||||
|
|
||||||
|
Frameworks should lure people into the [15]pit of success, where
|
||||||
|
following the normal rules and using normal techniques is the winning
|
||||||
|
approach.
|
||||||
|
|
||||||
|
I don’t think that React, in this context, really is that pit of
|
||||||
|
success. A naïvely implemented React SPA isn’t stable, or efficient,
|
||||||
|
and it doesn’t naturally scale to significant complexity.
|
||||||
|
|
||||||
|
You can add optimizations on top of it that fix those problems, or you
|
||||||
|
can use a framework like Next.js that will include those optimizations
|
||||||
|
by default. That’ll help you get pretty far. But then you’ll be lured
|
||||||
|
by all of the easy one-click ways to add bloat and complexity. You’ll
|
||||||
|
be responsible for keeping some of these complex, finicky optimizations
|
||||||
|
working properly.
|
||||||
|
|
||||||
|
And for what? Again - there is a swath of use cases which would be hard
|
||||||
|
without React and which aren’t complicated enough to push beyond
|
||||||
|
React’s limits. But there are also a lot of problems for which I can’t
|
||||||
|
see any concrete benefit to using React. Those are things like blogs,
|
||||||
|
shopping-cart-websites, mostly-[16]CRUD-and-forms-websites. For these
|
||||||
|
things, all of the fancy optimizations are trying to get you closer to
|
||||||
|
the performance you would’ve gotten if you just hadn’t used so much
|
||||||
|
technology.
|
||||||
|
|
||||||
|
I can, for example, guarantee that this blog is faster than any Gatsby
|
||||||
|
blog (and much love to the Gatsby team) because there is nothing that a
|
||||||
|
React static site can do that will make it faster than a non-React
|
||||||
|
static site.
|
||||||
|
__________________________________________________________________
|
||||||
|
|
||||||
|
But the cultural tides are strong. Building a company on Django in 2020
|
||||||
|
seems like the equivalent of driving a PT Cruiser and blasting Faith
|
||||||
|
Hill’s “Breatheâ€<C3A2> on a CD while your friends are listening to The Weeknd
|
||||||
|
in their Teslas. Swimming against this current isn’t easy, and not in a
|
||||||
|
trendy contrarian way.
|
||||||
|
|
||||||
|
I don’t think that everyone’s using the SPA pattern for no reason. For
|
||||||
|
large corporations, it allows teams to work independently: the
|
||||||
|
“frontend engineersâ€<C3A2> can “consumeâ€<C3A2> “APIsâ€<C3A2> from teams that probably work
|
||||||
|
in a different language and can only communicate through the hierarchy.
|
||||||
|
For heavily interactive applications, it has real benefits in
|
||||||
|
modularity, performance, and structure. And it’s beneficial for
|
||||||
|
companies to shift computing requirements from their servers to their
|
||||||
|
customers browsers: a real win for reducing their spend on
|
||||||
|
infrastructure.
|
||||||
|
|
||||||
|
But I think there are a lot of problems that are better solved some
|
||||||
|
other way. There’s no category winner like React as an alternative.
|
||||||
|
Ironically, backends are churning through technology even faster than
|
||||||
|
frontends, which have been loyal to one programming language for
|
||||||
|
decades. There are some age-old technologies like Rails, Django, and
|
||||||
|
Laravel, and there are a few halfhearted attempts to do templating and
|
||||||
|
“serve web pagesâ€<C3A2> from Go, Node, and other new languages. If you go
|
||||||
|
this way, you’re beset by the cognitive dissonance of following in the
|
||||||
|
footsteps of enormous projects - Wikipedia rendering web pages in PHP,
|
||||||
|
Craigslist rendering webpages in Perl - but being far outside the norms
|
||||||
|
of modern web development. If Wikipedia were started today, it’d be
|
||||||
|
React. Maybe?
|
||||||
|
|
||||||
|
What if everyone’s wrong? We’ve been wrong before.
|
||||||
|
Follow-ups & commmentary
|
||||||
|
* [17]"In defense of the modern web", Rich Harris
|
||||||
|
* [18]Friday Night Deploys (Podcast) #22: A Brief Discussion On The
|
||||||
|
State Of The Modern Web
|
||||||
|
* [19]Frontend First (Podcast): Read & Discuss
|
||||||
|
* [20]A Ready-To-Try Concept in Response to “Second-guessing the
|
||||||
|
modern webâ€<C3A2>
|
||||||
|
|
||||||
|
May 10, 2020 [21]@tmcw
|
||||||
|
|
||||||
|
References
|
||||||
|
|
||||||
|
1. https://macwright.com/rss.xml
|
||||||
|
2. https://macwright.com/atom.xml
|
||||||
|
3. file:///
|
||||||
|
4. file:///reading/
|
||||||
|
5. file:///photos/
|
||||||
|
6. file:///projects/
|
||||||
|
7. file:///drawings/
|
||||||
|
8. file:///about/
|
||||||
|
9. https://www.mapbox.com/mapbox-studio/
|
||||||
|
10. https://observablehq.com/
|
||||||
|
11. https://docs.mapbox.com/mapbox-gl-js/api/
|
||||||
|
12. https://github.com/observablehq/runtime
|
||||||
|
13. https://web.dev/interactive/
|
||||||
|
14. https://engineering.shopify.com/blogs/engineering/solving-the-n-1-problem-for-graphql-through-batching
|
||||||
|
15. https://blog.codinghorror.com/falling-into-the-pit-of-success/
|
||||||
|
16. https://en.wikipedia.org/wiki/Create,_read,_update_and_delete
|
||||||
|
17. https://dev.to/richharris/in-defense-of-the-modern-web-2nia
|
||||||
|
18. https://dev.to/devplebs/friday-night-deploys-22-a-brief-discussion-on-the-state-of-the-modern-web-2961
|
||||||
|
19. https://frontendfirst.fm/episodes/read-and-discuss-second-guessing-the-modern-web
|
||||||
|
20. https://medium.com/@kevinkirchner/a-ready-to-try-concept-in-response-to-second-guessing-the-modern-web-6946ec4d0598
|
||||||
|
21. https://twitter.com/intent/follow?screen_name=tmcw&user_id=1458271
|
||||||
Reference in New Issue
Block a user