Lessons From Building Real-World Frontend Apps
Intro
I have experience building applications from the ground up, often as the sole frontend engineer. That means every wrong decision I made was mine to fix—and I’d say I love that.
Everything below I learned the hard way—by making mistakes and then solving them. Eventually, I came up with my own rules for engineering new apps. They’re not new to the world—I just discovered them through personal experience.
This is the way.
Refactoring Isn’t Optional
Regular refactoring is a must. Code you wrote a month ago may already be outdated or awkward for current needs.
App grows. Requirements change. Features evolve. And if your code doesn’t adapt, it decays.
Failing to clean up as you go results in bloated, fragile systems. A simple change that should take 30 minutes might instead take days—because the code wasn’t refactored along the way
Don’t Over-Engineer—Feel the Balance
As you become more experienced, you’ll naturally want to write code that is clean, extensible, and modular. Principles like SOLID, DRY, and KISS guide us in that direction.
But they sometimes clash. Over-optimizing for flexibility can lead to unnecessary complexity. Over-simplifying can make your code unscalable.
There’s no universal rule for finding the right balance—you develop this intuition by building, breaking, and rebuilding over time.
I feel like I’ve gotten better at this, but I remember how it was in the beginning. I was reading about technical design, then design patterns. But I didn’t understand when or where to apply them.
Don’t Jump In—Draw It First
Not everyone can keep the entire app architecture in their head. I certainly can’t.
Before I implement something new, I sketch it out: component structure, data flow, API interactions, etc. It doesn’t need to be perfect—it just needs to make sense.
If you find your thoughts slipping while working through a design, pause and draw. This habit has saved me countless hours and headaches.
Decouple Backend and Frontend Types
In TypeScript-heavy projects, it's tempting to reuse the API's DTOs throughout your frontend code. Don’t do that.
Instead, map them into frontend-specific types as soon as they enter your app. This extra layer gives you insulation from backend changes—renamed and deprecated fields.
Even if it feels redundant at first, it’s a major win for long-term maintainability.
Don’t Be Afraid to Change Bad Code
I’ve seen developers wrap new features around broken old code just to avoid touching it. That’s how tech debt snowballs.
"If it works, don’t touch it" only applies when the code is clean, readable, and well-structured. Otherwise, you should touch it.
Here’s a real example: I joined a project where I found extensive usage of the setTimeout
function everywhere to “wait for things.” This approach can be fast for creating some POC if you need to show something real quick, but it should never be included in production code. Nothing wrong with setTimeout
, but often it is just misused. You can use it for common patterns like "debounce", "throttle", or "exponential backoff". But you should try to avoid using it when you have browser native alternatives. For example, sometimes you’ll find code that uses setTimeout
to wait for an element in the DOM, but it would be better to use the MutationObserver API instead.
The right move wasn’t to tweak the delay—it was to understand the underlying issue and fix the architecture. That’s what we must do.
Stay Current With the Web Platform
Frontend development evolves quickly. New CSS features, JS methods, and Web APIs roll out constantly.
If you stop learning, you fall behind.
Make a habit of reading web.dev articles, exploring MDN updates, and following changelogs. The more tools you know, the more elegant and modern your solutions will be.
Understand Design Systems (Not Just CSS)
Many projects suffer from chaotic, hard-to-maintain CSS—even when designers are involved.
To write good styles, you need to understand how designers think. Talk to them. Ask about color palettes, spacing systems, and naming conventions.
Realize that not every color in the design system is meant to be used. Patterns emerge: warnings are yellow, primary actions are blue, errors are red. The better you grasp the design logic, the more consistent and clean your styles will be.