In the world of web apps and mobile apps, there is a lot of planning, designing, and decision-making even mid-development. When you already have a complex system in place, new features can really break the app’s UX; or your code; or the app’s performance.
Let’s consider this example:
We need to implement a “favorite” feature on our posts. After saving a post as “favorite”, three other actions need to be made - triggering an email, a push notification, and updating the recommendation algorithm.
A “favorite” feature seems as easy to implement as it gets. But consider that the logic behind adding something to favorites can not only take a long time but also break after the user has already waited for the response. This is the point where we need to make a decision and it is as much a UX decision as it is an implementation one.
Many other scenarios can make you face the same decision. Things like having multiple microservices and the favorite action traversing quite a bit, or having an email trigger when someone favorites your post, etc.
To go a little bit deeper into this topic and have something visual to help you better understand the concept, I have also prepared a video here.
The app
Our app is very simple, but also very important to our customers (currently just me): it is a feed of meme templates. I was lucky enough to find Imgflip’s API and started coding a simple feed that fetches a number of meme templates, shows the title, image and also adds a favorite button for each post. At Wolfpack Digital, we mostly use Vue or Nuxt (here’s why), so I went with the same stack.
The like/favorite button was set up like a booby trap, as I always made the user wait for 3.5s, and after that, the action had a 50-50 chance to fail. This way, we were able to reproduce both successful and error behavior and see how our app would react in both these cases.
The natural first thought - that doesn’t really work
My first thought when implementing actions that might take a while (I think working with mobile apps has definitely influenced this a lot) is to add a loader and only update the UI when the action is successfully completed. If the action is quick, that is great, but what if the action is taking a long time? I will need to start thinking about blocking the user from pressing the same button again while it’s loading, also only “loading” a specific component/item, so the user’s UI does not get fully blocked from such a minor action.
Also, a significant issue would be error handling. The user hits the like button on a post, sees the loader, but decides to continue scrolling. After 2 seconds of only scrolling (or even worse, liking 10 other posts), an error message shows that the action failed. That would be confusing and frustrating.
Optimistic rendering - I got 99 problems but stopping the user ain’t one
Optimistic updates (a.k.a. Optimistic UI) is a pattern that we can especially see in frontend/UX development where different actions update instantly on the visual interface without waiting for confirmation from the server.
Github is one of the many companies that do this - I have seen similar examples from Twitter, Facebook, Instagram. When I hit the star icon, the UI automatically updates and the API call starts. But the UI is already changed as confirmed long before the response comes back as successful.
So let’s update our code! And actually, everything is pretty simple: instead of handling the UI update inside the .then after the await method that does the API call/action, we just update it before even calling. The only thing left to do now is error handling & updating the UI back if the action failed.
The result makes a lot more sense from a UX standpoint.
Error handling
You have spent the last few minutes reading about how we manipulate the UI and pre-fill everything assuming it would magically work. But we still need to set up some code to know if something failed.
The video below covers what I would consider as bad UX, but might be the best example of this pattern’s weakness: visually representing a failed action. The user usually moves on, scrolls further, and showing an error will just make everyone wonder which action actually failed and why they are getting out of their flow for something that happened a few seconds ago.
This does not mean that we shouldn’t handle errors, but we need to choose our behavior very carefully:
we could just log the error and never show the user visual updates in case of failure
another option would be a great error message (so we would know what action actually failed), but that is also very unobtrusive
giving the option to retry, like adding a small button for the certain area that failed
as soon as we find out that a call failed, update the UI back to the correct state; make sure no heart icon is left behind
silent retries are also an option, but there should be a limit
Important decision making
This pattern is not great in all situations. It replaces a bad user experience interaction - taking too long for interaction - with something that may be even worse, but happens a lot less. That’s why we need to choose wisely and be careful before going for it.
Of course, there are some good observations that we can take into consideration while going through the decision process:
avoid applying it for destructive/constructive actions - interactions that delete, create or edit an entry, especially if at the end of a long flow
this pattern works great with boolean actions - marking/removing something as favorite or liking/unliking
it is very important to apply this to reliable actions/API calls. For a small, binary action, but with a success rate of only 50%, this is definitely not a great option
the same applies if the API is always slow and takes a very long time to complete; we are trying to be optimistic, but it is not a great idea to let the user just move on and start a lot of actions, all with a successful UI update, before the previously called actions are confirmed as completed
sometimes a delay is expected (for example a change in permissions) and it is actually preferred to wait for confirmation before moving on
Conclusion
This pattern needs a very high level of communication with the design team, as it affects the UX of the whole app. The basic concept assumes a highly reliable and (hopefully) binary action will complete, updating the UI accordingly before the actual confirmation arrives.
Very similar optimistic patterns can be seen in multiplayer games or in concurrency (where multiple locks are managed with the calculated hope that they won’t interfere with each other).
I hope this article will help you make better decisions in the future. Also, if you’d like to keep learning, I suggest you check out this guide to understanding business logic as a QA specialist. You’ll find plenty of good tips on decision-making there as well!
insights
pack knowledge
Why Companies Outsource Software Development to Romania in 2026
Valentin Trif
Head of Business Development
Reading time: 14 min
Apr 28, 2026
Romania is a top 2026 destination for software outsourcing: 200,000+ engineers, native GDPR, AI-native delivery, and rates 40–50% below Western Europe.
What QA Looks Like When Your App Talks to Hardware
Ramona Rohan
Head of Quality Assurance
Reading time: 10 min
Apr 21, 2026
IoT app testing doesn't follow the same rules as standard mobile QA. Most testing frameworks are built around one assumption: the app is the product. When hardware enters the picture, that assumption breaks fast.
Q1 2026 Wrap-up: The Pack Hits Eleven
Cristina Strîmbu
Marketing Specialist
Reading time: 8 min
Apr 15, 2026
Q1 closed with momentum on every front. The pack marked its 11th anniversary, picked up a Webby Awards Nomination for ROAM-AI, took home two Web Excellence Awards for 3D2Cut and LoadHub, and saw our Founder & Co-CEO Georgina Lupu Florian named Regional Winner.