You
ReactTypeScriptFrontend

What 3 Years of Shipping Software Taught Me About React

After 40+ projects and three years in production environments, here are the React lessons I wish I had learned on day one — not year three.

April 10, 2026·6 min read

I started writing React when I was still figuring out what a closure was. I built forms that reset themselves, state that mutated in loops, and components that rendered fourteen times per keystroke. I shipped all of it. The clients were happy. The code was not.

Three years and 40+ projects later, I have a very different relationship with the framework. Not reverence — React is a tool, not a religion — but an honest understanding of where it rewards you and where it quietly punishes you if you aren't paying attention.

1. Component design is harder than it looks

Everyone talks about single responsibility. Fewer people talk about what happens when you violate it at scale. I once maintained a component that accepted 23 props. It rendered a button. The component knew about authentication state, theme, analytics events, modal state, and three different loading states. Touching it was a negotiation with the past.

The fix isn't just 'make smaller components.' It's thinking about what a component owns versus what it merely displays. Owned state lives inside. Display-only data flows in via props. Side effects go in hooks. When you draw that line clearly, the component stops fighting you.

2. TypeScript is not optional if you care about 3am

I resisted TypeScript for almost a year. It felt like ceremony — annotating things I already 'knew' were strings. Then a client's API silently changed a field from a number to a string. In plain JavaScript, you don't find out until the bug report. With TypeScript, the build fails and you fix it before it ships.

TypeScript doesn't slow you down. It transfers the pain from production to your editor, where it costs nothing.

The places where TypeScript feels annoying are almost always the places where your architecture is unclear. Use the friction as a signal, not a reason to add `as any`.

3. State management: use the boring option

I've used Redux, MobX, Zustand, Jotai, React Query, and combinations of all of them. My current default: `useState` + `useReducer` for local state, React Query for server state, and Zustand for the rare global UI state that actually needs to be global. That's it.

The mistake I made early was treating global state as the solution to prop drilling. The real solution to prop drilling is component composition — restructuring the tree so data doesn't have to travel through five layers to reach one component that needs it.

4. Performance is architecture, not optimization

Reaching for `React.memo` and `useCallback` everywhere is a sign that something is wrong with your component tree, not a sign of good practice. If a component re-renders too often, the question isn't 'how do I memo this?' — it's 'why does the parent hold this state?'

  • Code-split at the route level by default — lazy imports cost almost nothing to write
  • Images are always the first bottleneck. WebP, lazy loading, explicit dimensions.
  • Measure before optimizing. React DevTools Profiler shows you what's actually slow.
  • Bundle size matters more than runtime performance for first-load experience.

5. The lesson that took longest: ship, then iterate

The cleanest code I ever wrote was for a project that never launched. I refactored it three times, perfected the architecture, and then the client ran out of budget. Meanwhile, a project I shipped in two weeks with imperfect code has been running in production for two years and has made real money for a real person.

Code quality matters. But quality is defined by what the code enables, not what it looks like. A well-architected app that ships is worth infinitely more than a perfect one that doesn't.

SHARE THIS POST
Amal C P

Amal C P

Author

Software Engineer from Kerala, India. Specialize in full-stack web, mobile (Android/Jetpack Compose), and AI integrations, building robust, high-performance software.

Read Next

CONTINUE READING

Building an Enterprise UI Component Library with Jetpack Compose

At SOTI India, I built a Jetpack Compose UI library that cut feature deployment time by 99%. Here's what that actually meant — architecturally, technically, and in practice.

May 28, 2026
CONTINUE READING

My Android Journey: From XML Layouts to Jetpack Compose

I started Android development writing XML by hand and debugging RecyclerView crashes at midnight. Here's what changed, what I learned, and why modern Android is genuinely exciting now.

May 10, 2026
Avatar

Are you looking for the perfect solution?

Then you’re in the right place. Get the best solution you’re looking for. Just reach out and let me know!

Made with Designed by Enric S Neelamkavil