In any company, there’s an inevitable task: improvement work.
Every organization has its history (or legacy), and if this legacy is neglected, it can eventually become a significant technical debt. Therefore, I believe it’s beneficial to carry out improvement work whenever time allows.
I’m writing this post to reflect on the improvement work I conducted at my previous workplace.
When I first joined the project, the tech stack was as follows:
- React (CRA)
- JavaScript
- Redux Toolkit
- SCSS
Some might think, “Isn’t this already a modern stack?” However, a closer look revealed various issues:
- Dependencies that had to be forcibly installed with — legacy-peer-deps due to version conflicts
- Deployments taking over 7 minutes
- Environment-specific Dockerfiles
- Numerous API call codes without specified types
- A single global CSS file
The characteristics of tasks that needed improvement were:
- Currently causing bugs or hindering work
- Impeding usability (UX, DX)
- Being out of touch with market trends
Keeping these priorities in mind, I started with the improvements I considered High priority.
To list the tasks, I created the following list.

Improving Items Causing Bugs or Hindering Work
1. CRA to Vite Migration
This was a relatively easy task, only requiring proper handling of environment variables and SVGs. Removing CRA allowed us to clean up the project dependencies with minimal effort.
2. npm to pnpm Migration
After switching to Vite, build times decreased significantly, but not yet to a groundbreaking level. So, we changed the package manager.
Referring to the article “pnpm vs npm | pnpm” on pnpm.io, I learned that pnpm stores dependencies more efficiently than npm. We migrated to reduce build times, and it indeed significantly decreased (from 5 minutes to 2 minutes).
3. Dockerfile Modification
Previously, we had different Dockerfiles for each environment to inject environment variables. Moreover, even for Production deployments, NODE_ENV was set to ‘dev’ (in other words, the App’s environment variables and NODE_ENV were identically specified), causing errors.
We hadn’t noticed this earlier due to a lower Craco version and the use of hacky methods like force options or legacy-peer-deps during npm install.
Therefore, we consolidated the Dockerfiles into one, changed to a common method of injecting environment variables from the platform, and rewrote the Dockerfile to suit pnpm.
4. Authentication Structure Improvement
We enhanced security by moving from a fixed token authentication structure to one utilizing access tokens and refresh tokens. As a junior developer, I had tried various approaches: storing in localStorage, manipulating cookies on the client-side, and using sessions.
This time, we implemented a system where the server sets an httpOnly cookie and removes it upon expiration.
We used the same axiosInterceptor approach to perform token renewal a predetermined number of times when a token expiration response is received.
The server only sent a 401 response, but in future implementations, it would be good to designate an agreed-upon status code for token expiration.
While these may seem like simple tasks, they can be daunting due to their global nature. I realized it’s best not to postpone such tasks and to proceed quickly.
Enhancing UX/DX
1. Redux Toolkit to Tanstack Query
We were using Redux Toolkit without Redux Toolkit Query, which was challenging for me as a first-time Redux user. There was excessive boilerplate code, it took a long time to write a single API, and there was a high possibility of human error.
In the past, we sometimes managed server state with axios in the component itself, which I now think might have been easier to read.
Tanstack Query has overwhelming DX, which didn’t increase my motivation to learn Redux. So, I developed all new features and improvements using Tanstack Query and documented this for interested team members.
Seeing everyone prefer Tanstack Query over Redux confirmed that my choice wasn’t wrong.
However, the migration process was a bit tedious, and using a generating tool like hygen might have made it easier.
2. Usability Survey for UX Improvement
This was a memorable task. I created a survey of the project’s features and conducted it among in-house developers (those who volunteered).
We investigated everything from minor issues like menu names and color schemes, to which features were difficult and which parts seemed buggy.
It was enjoyable to compile the survey results, prioritize them, and solve them one by one.
We also conducted an offline usability study, giving task-based assignments to two junior employees unfamiliar with the product and observing how they performed these tasks.
We identified UX issues by noting where they got stuck or didn’t proceed as intended without prior information.
This experience of conducting usability studies and making improvements was quite impressive despite my short development career.
I plan to conduct online/offline usability studies and improvements in my own way, regardless of the company or its size, leveraging this experience.
Improvement Work to Keep Up with Market Trends
1. React Version Upgrade
Upgrading from version 17 to 18 wasn’t particularly challenging. It mainly involved changing the render function and modifying tsconfig.
2. Cleaning Up Deprecated Dependencies (e.g., Moment to Dayjs)
We cleaned up deprecated dependencies and upgraded versions. While these tasks can be time-consuming, they’re not particularly difficult (unless features are removed or types change significantly), so we could easily complete them.
3. Applying Zustand
We sometimes used Context API for simple tasks. However, we introduced Zustand for DX reasons. Zustand is really convenient, and the code is explicit.
The blog post “Working with Zustand” on tkdodo.eu was particularly helpful, especially the part about separating actions and state.
I noticed that even non-FE members who struggled with Context API easily accepted Zustand, confirming its excellent DX.
4. Applying CSS-in-JS
I often thought about how difficult it is to develop in 2024 with only global scss files and classnames.
I had been using Tailwind CSS since 2021, so working with these legacy methods now felt quite challenging.
As I believe FE should also do markup, I think it’s better for components to have their own styles.
Since our current project is an SPA, I introduced Emotion.js, which I think has good compatibility, and it significantly improved productivity.
Concluding Thoughts and Lessons Learned
In addition to these, we implemented Sentry to detect API errors and client errors (e.g., issues caused by Chrome extensions), conducted SEO measurements, and reduced bundle size through code splitting/lazy loading.
While these improvement tasks might seem obvious and unnecessary in well-established environments, I realized that they might not be so obvious for some teams.
I also confirmed that when making improvements, it’s important to introduce technologies that actually enhance DX, UX, or are meaningful to the project, rather than blindly following trendy technologies.
Although I’m leaving this company now, looking back at what I’ve written, I feel I’ve somewhat extended the lifespan of the product, which eases my sense of obligation a bit.