Replies: 1 comment
-
This can happen not only with loaders/actions, if you have a SPA and a separate API and the API change but users keep the old SPA open, then the API response will change. It's called version skew. It's in general an issue with any long-running client app that depends on an API that can be deployed and versioned separately. RR has some protection when you navigate around and detects a change in the client assets, then it cause a reload, but it's not for everything. If your app is usually left open for long periods of time, you need to ensure your server code doesn't have breaking changes, this is IMO the first thing to avoid, it requires extra care but it will prevent the client to break. Another thing you can try is to use skew protection on your infra, if you have v1 running and deploy v2, keep v1 running until it doesn't receive a new request in a safe time frame (like a day maybe), this way you can keep v1 running as long as users of that version of the client use it, once it doesn't receive any request stop running v1 on your server. To do that you also need to ensure your client v1 requests are sent to your server v1 correctly. Another thing you can do is to detect there's a different version of the app deployed and let users know they need to reload. You can see this on Discord where they show a small button on the top right corner that basically reloads the app so you get the new client version. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi team!
I left this category as general since this post is both Q&A and a call-out for others who might run into the following situation.
Overview
I've noticed a hidden complexity that isn't really talked about with Remix/React Router's loader/action paradigm. This particularly applies at scale for long-lived applications. This complexity arises when an existing application needs to modify or relocate these server functions.
The core issue: During rolling deployments, users can hit mismatched client/server code, leading to runtime errors or broken functionality. Below are three scenarios that illustrate this problem.
Scenario A: Adding Fields to Loader Data
An application is running with multiple instances behind a load balancer. The application has route
/foo
with a loader function that returns the following data:Now the team wants to add new functionality to the client side of the route via a new field in the loader data:
This looks like a fairly harmless change, they're just adding a new field, but a sneaky issue arises during deployment. Some users end up with mismatched client assets and loader data:
baz
being undefinedScenario B: Adding Required Fields to Actions
The setup is similar: an application running with multiple instances behind a load balancer.
In this scenario we have route
/foo
with an associated action that expects the following payload:Now the team wants to add new functionality to the action by requiring a new field:
Again, this looks like a harmless change. The team updates their client-side form to add the required "age" field, but a similar issue arises. During deployment, users with old static assets hit new instances and fail to complete the form because they're not sending the
age
field.Potential solution: Better versioning or moving this action to an "api" route so you can version the API while keeping the client route unchanged. I'm flagging this because there are no clear guidelines around extending actions, and it's not immediately obvious that you might be breaking your action during deployment, even when you own both the frontend and backend.
Scenario C: Moving Actions Between Routes
In this final scenario, an application has a child route
/foo/bar
with an action function. The team wants to add a new child route/foo/baz
, and bothbaz
andbar
need access to the same action. The most obvious solution would be to move the action from/foo/bar
up to the parent route (/foo
) so both child routes can access it. Again, we fall into a problematic situation.During deployment:
/foo
/foo/bar
but hits a new server instance that has already moved the action to/foo
, encountering a missing action errorOur solution: A three-phase deployment:
/foo/bar
to/foo
(both exist simultaneously)/foo/bar
to use the action in/foo
/foo/bar
This approach ensures an action is always available during the transition period.
Summary
These scenarios highlight hidden gotchas that can occur between actions/loaders and client code during rolling deployments. The fundamental issue is that zero-downtime deployments can create temporary version mismatches between client and server code.
I'd love to hear how other teams handle these scenarios without degrading the user experience:
For context: versioning every route seems impractical for long-lived apps, especially when users share those links. Imagine if GitHub had to version their repo routes every time they changed a loader... it would break the web!
Looking forward to hearing your thoughts and solutions!
Thanks all!
Beta Was this translation helpful? Give feedback.
All reactions