DEV Community

Pranav Sindura
Pranav Sindura

Posted on

Stop Abstracting Your Frontend

TL;DR:
We treat frontend like backend - abstracting UI into patterns that don’t match how designs actually evolve. Stop trying to make your UI scalable. Instead, make it adaptable. This post breaks down why your frontend abstractions keep failing — and what to do instead.


After working across several UI applications from scrappy personal projects, startups-scale mobile applications and to Big Tech enterprise CRMs, one thing keeps bothering me: we treat frontend like backend.

I’ve studied all the usual design patterns to clear interviews: factory, strategy, builder, proxy, facade... the works. And I’ve seen these abstractions leak into frontend codebases where they don’t belong.

We try to scale, abstract, and over engineer - and we forget that UI doesn’t reward elegance. It rewards the ability to iterate, and to adapt.

The issue? People are too eager to treat UI the same way they treat backend systems - with abstraction, encapsulation, and DRY principles - without acknowledging that the rate and nature of change in UI is fundamentally different.


UI is Designed to Break

The most fragile part of your stack is your UI. One small tweak in Figma can undo days of “architectural scaffolding”. Yet people still keep trying to create “smart” abstractions: reusable components, nested sections, configurable UIs with metadata-driven rendering.

It sounds elegant. Until it isn’t.

Yes, you should abstract at the foundational level: design tokens, layout primitives, interaction patterns - the building blocks that rarely change.

I love:

That’s where abstraction belongs.


Figma Doesn’t Care About Your Abstractions

Designers don’t think in patterns.

They think in flow, hierarchy, narrative. When they move a section around, introduce a banner between specific bullets, or redesign one screen - your abstraction breaks.

So you patch it. You add metadata. You build config-based rendering. You invent more patterns. Before long, you’ve created a custom CMS inside your React app.

Before your content payload looked like -

[
    "This is point #1",
    "This is rendered as bullet point list within a section",
    "Great!"
]
Enter fullscreen mode Exit fullscreen mode

Now it looks like -

[
    { type: "BULLET", content: "This is point #1" },
    { type: "BANNER_SMALL", image: "https://link.to/the/small/banner", alt: "this is an image of your small banner", maxVersionToShowOn: "1.9.0" },
    { type: "BULLET", content: "Not so great :(" }
]
Enter fullscreen mode Exit fullscreen mode

Then the new engineer arrives. They need to implement a small change. But first, they have to reverse-engineer your abstraction. They ask:

“How is this page rendered again?”
“Why is the footer wrapped inside a section factory?”
“Why is the banner conditionally injected via content metadata?”

The answer is: because the team prematurely abstracted everything into patterns that looked elegant once, but are painful forever.


The Cost of Abstraction is Underestimated

Design changes more frequently than business logic.

Your database schema might stay stable for months. Your Figma file changes every 2 weeks.

But frontend abstraction assumes the opposite: that content is stable, and structure is sacred.

That’s just not how UI works. The result?

  • Engineers fear touching the UI
  • Pages get rebuilt from scratch
  • Abstractions go stale
  • Designers are told: “This isn’t feasible”
  • Innovation slows down

UI is a Canvas to Iterate, not a System to Scale

We need to stop over-engineering the frontend. Your UI doesn’t need to be scalable. It needs to be navigable, editable, and fast to iterate on.

  • Favor composition over abstraction
  • Favor readability over DRY
  • Favor changeability over structure

KISS - Keep It Super Simple.

Treat design as the primary driver. Build for change, not for elegance. Your abstraction won’t save you when the banner moves.


💡 Takeaways:

  • Abstract slowly, especially in the UI.
  • Prioritize readability and adaptability.
  • Align your component design with the rate of change in design, not backend logic.
  • KISS still wins — even in 2025.

Top comments (14)

Collapse
 
xwero profile image
david duymelinck

Designers don’t think in patterns.

While I am not a designer. In order to keep an overview I think they do need to keep an eye on patterns to create a consistent design.
There can be flourishes to make the design exciting, but they start from a base.

Most design patterns goal is to make it easier to adapt and iterate. So I think the idea is good, but the implementation does not fit the job.

I recently written a post about structuring CSS. And there I proposed an idea that keeps the base styles as a group; card, teaser. But the actual CSS are classes that directly translate to the design.
No card article featured, where each have their specific styles, just card-article and card-featured with all their relevant styles.
To prevent too much style duplication, I would create a CCS file per page type, instead of one CSS file for the whole site.

It is one idea, but I think there are many other routes to make it happen. Maybe even better than my idea.

Collapse
 
pranavsindura profile image
Pranav Sindura • Edited

I would say the patterns designers think in are UX patterns. Things like - navbar for common links, drawer style navigation vs tab style navigation, dropdown vs combobox style options, side panel vs dialogs. Something like selecting a dropdown item leads to opening a side panel might be considered a bad pattern.

It is absolutely important to keep the design cohesive and consistent, simply because otherwise users get confused and that leads to added burden for both engineering (live site incidents) and business/product (dropoffs).

How we as engineers implement those UX patterns is something that we own entirely.

As for the design implementation aspect, i love how quickly things can be prototyped with utility style classes, but as project grows the duplication grows too, and it might be worth considering having custom set of styles built from base utility classes, which then as time goes on forms into a theme or a self owned design system + implementation library.

But people forget - Designs are expected to change, and we should not be afraid to innovate.

Collapse
 
xwero profile image
david duymelinck • Edited

The problem I have with utility class frameworks is that it should be a temporary way of working, but it has become a permanent solution.
Abstractions are a part of the cause for that behaviour.

The key is finding a language both parties, designers and frontend devs, agree on. That will make the job easier for everyone.

Thread Thread
 
pranavsindura profile image
Pranav Sindura

Haha, I'm guilty of that too. It's a trade off right? Speed of iteration vs Readability of code maybe. But you're right, i have seen decently large projects still working with utility based styles and they overflow my screen, having many redundant ones together and nobody touches them.

The key is finding a language both parties, designers and frontend devs, agree on. =>
100%, designer's insights are important and somehow i feel the frontend devs don't talk about it with them enough. It would solve many issues.

Thread Thread
 
xwero profile image
david duymelinck

Sure I was also excited when Tailwind came out. but then I started to weight the consequences.

As you mention a whole bunch of classes are daunting to change, and that is when people look for "clever" solutions to keep on adding more utility classes. Rather than going back to the base and figure out why all those classes are needed, and how to make it less cumbersome to make changes.

Collapse
 
kc900201 profile image
KC • Edited

@xwero

I recently written a post about structuring CSS. And there I proposed an idea that keeps the base styles as a group; card, teaser. But the actual CSS are classes that directly translate to the design.

I'm interested in this. Would you like to share about the post?

Collapse
 
xwero profile image
david duymelinck

here. It is just an idea, no code.
If you use a CSS preprocessor a mixin would be my go to to implement this.

Collapse
 
derstruct profile image
Alex • Edited

Finally, a quality article, thanks.
I will slightly disagree — without abstractions, change implementation also struggles. It's much easier to keep the mental model of an abstracted code, so iterations are faster and require less debugging.
"Abstract slowly" partially addresses this point. But it's more precise to say: start with micro abstractions, don't bother to rewrite, and stable patterns will emerge.

Collapse
 
pranavsindura profile image
Pranav Sindura

Absolutely, patterns emerge. And some patterns are consistent across applications as well and become a no-brainer to abstract (Ex - Navbars, Tab navigators, create vs edit forms, etc).

Within the "lifecycle" of an abstraction, there comes a time to ask - should i extend it? Or am I creating a mess? Often it is too late by then and creating a new abstraction becomes a challenge on its own (time constraints are inevitable).

My take is still, even with micro abstractions, it helps to understand how the UI abstraction is expected to evolve, and often the Designers are the one who can answer it. We don't do that enough.

There will however be trade offs no doubt about it. Micro abstractions may pay off just as well.

Collapse
 
derstruct profile image
Alex • Edited

If we extrapolate your statement to its extreme, the best way to program UI is by writing long scripts containing disposable code. It can work in the short run, but it pulls everything and everyone into the abyss for apparent reasons.

I can relate to the recent "don't abstract" trend, but from my perspective, the issue behind over-abstraction is more psychologically related. A developer's brain tends to create structures just for the sake of structure or perfection, or because it's right, when the right way is yet to be discovered. I am guilty of that, too.

However, what you're offering leads (not directly, article is balanced) to essentially the same issue: "do something as a rule, not because it fits this particular context." Sooner, we will see articles like "Top 5 abstraction patterns every developer should use", and again, and again, without addressing the essence of it.

Collapse
 
dotallio profile image
Dotallio

100%. I've wasted way too much time untangling fancy abstractions when all we needed was simple, readable code that could change fast.

What's one practical tip you have for keeping things adaptable on complex projects?

Collapse
 
pranavsindura profile image
Pranav Sindura

I've experienced that a lot of times engineering team does not talk to their design team enough. Like how we have discussions about building "reusable components", they have discussions about building "reusable UX patterns". And we build the components first but forget that the prerequisite is the reusability of the UX pattern.
A simple practical tip - Set up an Engineering-Design bridge and understand their end, they know when the patterns are maturing and can tell you to a great extent what is probably going to be reused and what is not.

Collapse
 
leo3280 profile image
Arjun Saini

Not much of a regular article reader, but the title really caught my attention and I agree with it to a certain extent. While my experience is mainly with native Android development, I think the idea applies there too.

Of course, avoiding abstraction entirely isn't realistic; the key is defining thoughtful boundaries, especially in larger, testable projects.

Appreciate the quality write-up!

Collapse
 
elsyng profile image
Ellis • Edited

Fully agreed. Keep frontend super simple. As few abstractions and layers as possible.

Frontend and backend are two different beasts. Very different purpose, environment, and business/performance priorities. If you find yourself having to implement complex code and structures in the frontend, then take a step back and re-think the whole. Frontend is for input and display of data, and to make it as simple, beautiful, easy and intuitive as possible. Backend is for business logic, data validation, data storage and exchange, etc.

A lot of developers jump from backend into frontend and start "programming" it as if it is backend. Then you end up with heavy, slow, complex, unmaintainable frontends. It can take several years for a backend developer to understand how frontend is different, in my experience.