As I see it occuring so many times in the industry, it so happens that many developers do anticipate performance issues while still in early development stages of an application project.
And this, even prior to running the app on a production-like environment !
This strategy often ends up being the same as shooting oneself in the foot.
Why you say? Because optimizations always come with a cost!
Use case: code-splitting
Is spreading the total loading time of the app always a good idea? Is splitting the code and lazy-loading the different chunks, always a good solution? Every time? No matter what?
Well:
- Are those chunks really heavy enough to justify having the spinning wheel of death appear everytime the user clicks on a link?
- Or should you rather have an app displaying a longer intial loading time but which, once fully loaded and cached, responds swiftly to all user interactions?
- Are your end users from the general public, or are they B2B customers? Sure users in general don't like to wait of course, nobody does, however:
- B2B users might not have a choice than to be using your app, so if there is no budget for optimization, then intial loading time can be "imposed" upon them with minimal business repercussions;
- On the other hand, public users, C2B users, WILL go away and shop somewhere else if your e-commerce app takes too long to load, having a real impact on business revenue in the short or medium term
What to look out for
- After a hard reload, meaning you got rid of all cached elements, and after applying a given optimization, is the app starting faster during its initial render?
- Is this really the only criteria to consider ?
- What are you trying to achieve?
- Do you want users to feel that your app loads fast?
- Do you want the app to respond quickly to user interaction?
Sure! We all want both, but again, at what cost?
But most importantly :
- does it depend only on what YOU want or think as a developer ?
- what if the target machine is a smartphone, can you afford to have many http requests, knowing how it impacts battery life ?
- what if there is no budget for optimization ?
- what is the hosting plan in production, and what is the cost related to hitting your server by an increased amount of http requests ?
So what is the solution?
We must be able to evaluate the cost of the optimization by clearly identifying what was lost and what was gained.
It's all about the tradeoffs baby!
Step #1 - Diagnose
So, let's first run the app we are working on, and IF we find that the app starts too slowly, or a certain component takes too much time to mount or render, THEN let's diagnose the problem.
How?
- The React devtools Profiler or the React Profiler API are examples of a tools which might help identifying long re-rendering times.
- The network devtools might also help identifying heavy JS or CSS resources.
The build report provided by the create-react-app build script also provides a list of chunks along with their gzip file size
And let's not forget the almighty Lighthouse, another one of the Chrome devtools.
Step #2 - Measure
So let's measure the problem.
- How much time do components take to mount or render?
- How much time is a function taking to run ?
- Is there some JS or CSS resource blocking the intial app rendering ?
Check out Progressive React for details on measuring using different tools.
Step #3 - Identify & Apply optimization
After diagnosis and measurement, the right optimization strategy should form. This allows to identify the appropriate optimization technique(s) for the particular situation we're trying to solve. So, here's when we apply an initial solution.
Step #4 - Measure again & Compare
Lastly, let's measure once again to ensure that the optimization cost was worthwhile.
How? By comparing the new measure to the original measure, the one we took while diagnosing the problem.
Conclusions
All in all, these are examples only of considerations to take into account even before we start optimizing just for the sake of it.
The right optimization strategy is far from being a "simple" technical-only subject, and depends at least on:
1) budget,
2) the nature of the app,
3) end users (& stakeholders at large),
4) the end users' target machines
5) technical requirements of the production environment
Final words of caution about optimization in general
So, let's not optimize for the sake of it.
Meaning, let's not memoize or avoid inline objects systematically (React.useMemo
& React.useCallback
).
Let's not split our code and lazy load it systematically (React.lazy
+ React.Suspense
+ Spinner
+ ErroBoundary
).
Let's not memoize components systematically (React.memo
, React.PureComponent
, shouldComponentUpdate
).
In order words: consider the act of optimizing ONLY if there is a performance problem or complaint in production!
Bonus: Recommended Toolchains
The React team primarily recommends these solutions:
- If you’re learning React or creating a new single-page app, use Create React App.
- If you’re building a server-rendered website with Node.js, try Next.js.
- If you’re building a static content-oriented website, try Gatsby.
- If you’re building a component library or integrating with an existing codebase, try More Flexible Toolchains.
Top comments (0)