Ok, Iβm back from my short vacation and returning with some useful content π As you know, from time to time I write posts for you in the style of a...
For further actions, you may consider blocking this person and/or reporting abuse
Sylwia you always found some really interesting point like now a custom header, congratulation I never know that is exsist.
I did the file splitting in my - no framework - solution, thanks for the build, tailwind and the minimal rective code size, split a program to independent html pages.
This is actually a beautiful example that you can still achieve a very elegant bundle structure even without a framework π Those sizes look really nice.
And yeah, the preflight/CORS behavior is one of those things that surprisingly many developers never really hear about in detail. I honestly feel like itβs discussed way too rarely, and the comments under this post kind of confirm that π
Definitely
Great advice, as always! And this is something nobody thinks about: performance isn't just about well-structured code and optimized SQL queries, but also about best practices that we all tend to forget. I try to apply the principles you mention, and generally, my sites have Lighthouse scores above 90. And yet, guess what: I still sometimes forget to optimize the size and file size of an image, or to defer the loading of a script or a fontβ¦
Thanks, Pascal π Oh yes, images were often one of the last things I optimized too π
Although a few years ago I worked on an app that had to function both in Western Europe and somewhere in the middle of Africa, sometimes in libraries with extremely poor internet connections. That project really trained me hard in these kinds of performance issues π
I think this covers 99% of everything. The problems with deciding whether to split your giant JS bundle go away if you don't use it, because unless you're writing a DAW or an office suite or something, you're almost certainly solving problems that don't exist.
Thereβs definitely some truth to that π Thankfully, it does feel like the industry is slowly moving back toward minimalism again.
And honestly, loading giant fancy images everywhere is often pretty absurd nowadays π The early-2000s βevery website must look like a movie introβ era should probably stay in the past π
I only read the first two mistakes mentioned; I know i don't need to read the rest. I've messed up. Custom headers are often used in developer mode to bypass CROSS, but are often forgotten to be properly set up on the move. Once I write a function that calls auth for the whole apps, and when it called it runs the other function in the background with the idea it will be ready anytime i want. But the startup is as slow as a snail.
Exactly π Sometimes itβs just one tiny thing somebody added, forgot about, or βtemporarilyβ hacked togetherβ¦ and suddenly the whole app feels weirdly slow π
And then the entire team spends days staring at the network tab wondering what on earth is going on π
and right now... vibe coding makes it worse... I mean, i join one volunteer project, many are students, and many just use full AI from drafting to deployment using agent, and when some thing wrong... you know that it is like finding a nail in a haystsack.
Yeah, exactly π And it becomes even worse when people donβt really have the theoretical foundations yet, because then debugging turns into absolute chaos.
If you fully rely on agents from drafting to deployment, but donβt really understand whatβs happening underneath, performance issues become a complete nightmare to untangle π
Performance must be a top-tier checklist item for any eco-conscious developer. Every kilobyte saved in a bundle and every millisecond saved in execution is energy that isn't being wasted.
"Sometimes the fastest image is simplyβ¦ no image π"
I really love this perspective π Exactly, performance isnβt only about happier users or better business metrics. Itβs also about efficiency and avoiding unnecessary waste. Every oversized bundle, unnecessary request, or giant image means extra energy consumption somewhere in the chain. And when millions of users open an app, those βtinyβ decisions suddenly stop being tiny!
"OPTIONS requests appearing before almost every API call" - I noticed that in a project of mine, and then we decided (in the end) to simply host the API and the "app" (SPA) on the same subdomain - meaning, no CORS ...
But reading your article makes me think it might actually have been solvable with CORS (so with the two separate subdomains) ...
P.S. if it's because of a "custom header" (but what exactly is that?) then it might have had something to do with the AWS environment we were running in (Cloudfront etc) and maybe there wasn't much we could do ...
Yeah, in your case moving everything under the same origin may honestly have been the safest and simplest solution.
And by βcustom headerβ I mean headers we add ourselves in the frontend, often globally in some interceptor, things like X-Feature-Flag, X-Client-Version, X-Debug, etc. Those can trigger preflight requests even for normal GET calls.
But to be fair, preflights can also happen because of things like Authorization headers, PUT/PATCH/DELETE methods, or even application/json, so depending on your setup there may indeed not have been much you could realistically avoid π
Thanks! That more or less confirms what I thought (well, based on what you explained in the article) - we do indeed have those auth headers, and probably some other ones as well, so there might indeed not be that much we could do ...
Our backend was/is hosted in the US (East coast), and I'm located somewhere half around the globe, and I could REALLY notice an annoying amount of latency in UI responses and API requests - so then I started checking the network tab, and saw those pre-flight requests ...
In the end, apart from getting rid of CORS, we optimized a lot of other things, almost eliminating all of that lag - so that made the difference between an "annoying" UX and a fairly pleasant one ...
Yeah, thatβs exactly how it usually goes π At first itβs very easy to blame βthe networkβ or βthe backend,β but once you start digging into the details, all kinds of weird little things suddenly appear.
And honestly, I think this is also why AI probably wonβt replace us that quickly π Real-world performance issues are often this messy mix of infrastructure, browser behavior, historical decisions, accidental complexity, and random enterprise archaeology π
Hopefully we survive until retirement π
Yes, we will survive! Isn't that the title of a famous song? No, it's "I will survive"! :-)
Hahahaha this just reminded me of a story π A Polish couple I know once wanted to use βI Will Surviveβ as their first dance song at their wedding xDDDD
When I explained to them that the song is literally about a breakup, they suddenly looked very concerned and decided to search for another one instead π
Less is more sometimes. This is a good reminder that performance problems usually donβt start as huge failures, they stack up from tiny choices.
Exactly π One small mistake here, one tiny oversight thereβ¦ and suddenly the whole application starts feeling sluggish π
Welcome back home @sylwia-lask
Hahaha thanks π I missed you guys too π
These are really nice tips for improving performance quickly and without huge effort π
I usually put code quality and readability before raw performance. Highly optimized code is great, but if the team is afraid to touch it because nobody fully understands it, thatβs a highway to hell. So the code splitting example sounds way too familiar to me, but shh π
Oh, absolutely π Premature optimization can become a complete nightmare for maintainability. And honestly, on the frontend, good practices plus modern ESNext/browser features already solve a huge amount of problems.
Alsoβ¦ the βtheoretical code splittingβ situation is incredibly common π
This is a really practical breakdown.
The βdeath by a thousand cutsβ point is exactly how many production problems actually happen. One old custom header, one shared module that quietly pulls half the app, one unnecessary dependency, one huge hero image β none of them looks catastrophic alone, but together they shape the user experience.
Iβm seeing a similar pattern while building NEES Core Engine, but on the AI runtime side. Small unmanaged choices around context, memory, tools, fallbacks, and traceability donβt look dangerous individually, but over time they turn into reliability problems in production AI products.
For frontend performance, we use network tabs, bundle analyzers, and real-device testing. For AI products, I think we need the same mindset: runtime visibility, governance, and traceability around what actually happens after deployment.
Great article β very practical and grounded.
Thanks for this comment. Honestly, this is such a great contribution to the discussion!
And I think youβre absolutely right. AI products already have β or soon will have β very similar kinds of problems. Different technology, but the same βdeath by a thousand cutsβ effect caused by lots of tiny unmanaged decisions.
Which is honestly kind of fascinating π We move forward technologically, but a lot of the old engineering knowledge still turns out to be extremely useful in these new environments too π
Thank you β I really liked the way you framed the original post.
That βdeath by a thousand cutsβ pattern is exactly what I think AI products are starting to face too.
In normal apps, tiny unmanaged decisions can slowly damage performance, reliability, and user experience.
In AI products, the same thing can happen through:
Individually, each issue looks minor. But together, they create drift, unpredictability, and trust problems.
That is why I think a lot of old engineering lessons still matter in AI systems. Observability, boundaries, reviewability, and clear contracts are becoming just as important as model quality.
Different technology, same engineering truth: small unmanaged decisions eventually become system-level failures.
The custom headers triggering OPTIONS preflight requests is a detail I had completely overlooked, and it explains a lot about the latency I have been seeing. Your point about lazy loading being defeated by a bloated shared module hit home, since our codebase likely has the exact same trap. I need to stop assuming tree shaking will automatically save us from importing entire libraries. That oversized hero image silently killing LCP is another mistake I am probably guilty of.
Hahaha honestly, I think every developer who actually ships real applications has been guilty of at least one performance disaster π Probably the only people who never make these mistakes are the ones who never build anything π
Love this breakdown β itβs the perfect reminder that performance issues are rarely one giant bug, but rather a pile of βtiny cutsβ that add up.
The preflight request example especially hits home. Iβve seen teams chasing backend latency for weeks only to discover it was an immortal custom header triggering OPTIONS calls. Same story with code splitting β looks great in theory, but if everything funnels through a bloated shared module, youβre just shipping one giant bundle in disguise.
Your point on dependencies is gold too. I once audited a React app that had three different date libraries plus Lodash imported wholesale. The bundle was basically a museum of developer preferences.
And the background image example is the classic βworks fine on my machineβ trap. On a fiber connection itβs invisible, but on a cheap Android over 3G itβs the difference between usable and abandoned.
The way you framed it β death by a thousand cuts β is exactly right. Individually harmless, collectively devastating. Thanks for surfacing these; theyβre the kind of practical lessons that save real users pain.
Thank you so much for this comment π And honestly, I think it perfectly shows how incredibly common these problems really are.
Almost every larger application seems to accumulate some combination of βimmortalβ headers, giant shared modules, forgotten dependencies, or oversized assets over time π
Performance bugs are basically archaeological artifacts. You remove one random
X-Legacy-Feature-Flagheader and suddenly Lighthouse goes from βneeds improvementβ to βwho touched production?β πHahahaha exactly π Itβs the same with code splitting sometimes. Suddenly the app loads way faster and everybody starts asking: βWaitβ¦ are we sure something isnβt missing?β π
Thatβs the universal performance optimization panic π
βApp is 40% faster now.β
βGreat.β
ββ¦but what did we accidentally break?β π
Hahaha 100% π And the worst part is when something suddenly starts working βby itselfβ after the optimization π
Great breakdown .!
The death by a thousand cutsanalogy is spot on - individually, these seem harmless, but together they can make an app feel terrible on real-world devices.Exactly π Thereβs rarely a single silver bullet in performance work, you usually have to dig into lots of tiny details.
And honestly, this isnβt only about user satisfaction anymore. Performance is also a form of accessibility. Not everybody has a powerful laptop, fast internet, or a modern phone π
This is such a sharp, practical list. Itβs easy to focus on the 'big' architectural decisions and completely overlook how quickly these 'tiny' mistakes compound into a sluggish user experience.
Your points on image optimization and excessive re-renders hit home particularly hard. Iβm reminded of a conference talk I gave where I accidentally included a few of these mistakes in a single demo. It ran like a dream on my local machine, but the moment I hit the conference Wi-Fi, the app basically turned into a very expensive slideshow.
Thereβs nothing like 'real-world' latency to turn a developerβs 'minor oversight' into a performance post-mortem! Thanks for the great reminders to keep us honest.
Thanks so much for the great comment π And ohhh yes, conference Wi-Fi is always an adventure π
I totally relate to that fear. Before my last conference talk, I literally added service workers to everything the day before and ended up running the demo remotely/offline-ready just to save myself from disaster π It absolutely saved my ass in the end π
Your posts always amaze me β₯οΈ
AWWW thank you so much β€οΈπ
Reading this felt way too relatable π
Sometimes in tech and honestly in life too, itβs never one huge mistake, but hundreds of small ignored things that slowly create bigger problems over time.
Really loved how this article turns simple performance lessons into something every developer can connect with personally
Thank you π And honestly, thatβs exactly why I wanted to write it this way. Performance problems are often very technicalβ¦ but the pattern behind them feels surprisingly universal π
I've definitely been guilty of at least one of these π
The one that hit closest was #2 I spent an embarrassing amount of time debugging a slow list component last month Turned out I was missing a proper key Two hours Because of one line
That's the thing about tiny mistakes They're tiny to write They're not tiny to debug.
Thanks for the checklist bookmarking this for the next time I wonder why is this slow?π
Hahahaha exactly π Honestly, two hours is still pretty good π Sometimes people spend weeks trying to figure out why something feels slow.
And thatβs what makes performance issues so tricky. The actual mistake is often tiny, but the debugging journey absolutely isnβt π Thanks for the great comment!!!
Wow, a blast of knowledge this was for me.
I've always had a strong focus on images, and I've definitely experienced the frustration of images loading slowly on e-commerce and government websites.
In one of my projects (which I've since abandoned π ), I learned heavily about the importance of effective image loading. Using formats like WebP and AVIF really helps reduce image sizes while ensuring the quality remains intact.
Hahaha, I worked with WordPress sites a long time ago, so I was basically trained in image optimization by force π
And yes, WebP and AVIF can make such a huge difference, especially on image-heavy websites π
This is one of those posts that should be bookmarked. The practical examples really help bridge the gap between 'I know the syntax' and 'I can use this in production.'