My first encounter with React? Traumatic.
Okay, maybe not traumatic in the real sense, but the shock was definitely real. It was love at first sight, sure, but also a painful reality check.
At the time, I was working for a company where the codebase was 100% PHP. No framework. No structure. No system. Just procedural logic and a handful of shared functions doing the heavy lifting. I knew it wasn’t ideal, but I wasn’t in a position to change much. All I could do was keep my code tidy, reusable where possible, and spend my evenings studying modern tools at home.
This was during the golden age of frontend experimentation—when job listings were full of calls for “component library developers,” and new frameworks were being born every week. I spent my nights exploring React, Vue, Angular… and I fell hard for the clarity of React.
I loved how quickly I could spin up a project and get something meaningful going. The separation of concerns made sense. The reusability was addictive. Even the boilerplate felt elegant. Compared to my day job, it was another world.
So I doubled down on learning it, convinced that I’d found my path forward.
Eventually, I landed a job at an agency building complex apps in Next.js. I felt ready. I'd done the tutorials. I’d built some side projects. What could go wrong?
Well… everything.
There was no trace of the clean, predictable codebases I had fallen in love with. The real-life React code was a jungle—deeply nested components, massive files, endless wrappers, unfamiliar libraries, sprawling interfaces, custom hooks I’d never seen before.
And my clean little components? They didn’t stand a chance.
I quickly realised just how naive I’d been. My side projects had never pushed me into the weeds. I knew the syntax, but I didn’t have the experience. And in production, it shows. That was my biggest lesson: tutorials teach you the “what,” but production teaches you the “how” and “why.”
The first challenge that stumped me was component granularity.
In my toy apps, each component had a clean scope. One job, neatly done. I didn’t even think about it. But here, components were doing five different things at once, juggling state, API calls, business logic, rendering, side effects. And no one had a simple rulebook to follow.
That’s when I started asking everyone around me—seniors, leads, colleagues—how they structured their components. What their criteria were. How they split concerns. And what surprised me most was their answers: there wasn’t a single right way. It wasn’t about following some hidden standard. It was about experience. You start to feel when something is off, when a component is trying to do too much, when state should be passed down—or kept isolated.
That’s when things clicked.
Over time, I started seeing components differently. I rarely write return blocks that are dozens of lines long anymore. I avoid crowding a component with multiple pieces of state, sprawling logic, or functions that should clearly live somewhere else. I now scope my responsibilities tightly. I try to make each piece clean, composable, and above all, maintainable.
And the result? My libraries are easier to read. Easier to reuse. Easier to fix. When something breaks, I can find it faster. When something needs to be extended, it’s not a nightmare. I can’t fully explain how it happened, but at some point I developed a kind of radar—like I can see when a file has grown too big or when logic needs to be split. That instinct wasn’t taught. It was earned.
More importantly, I stopped writing code like it was a personal artwork.
I started treating my code as something shared—a tool that other people (and my future self) would need to understand, use, and maintain. That shift in mindset changed everything. My priority became clarity. Not cleverness. Not purity. Not perfection. Just a clean enough implementation that helps others (and myself, three months later) succeed.
And let’s be honest: there’s no worse feeling than opening one of your old components and thinking “who wrote this mess?”—only to realise it was you.
So now I always try to write with others in mind. I think about the next dev (or me on a bad day) who’ll read the code. I try to leave behind something usable, something that reduces friction instead of adding to it.
Because if you’ve ever inherited a messy component, you know exactly how frustrating it is.
And if you’ve ever written one, you know how easily it happens.
Help your teammates. Help your future self.
Write code like it’s not just yours.