Hacker News new | past | comments | ask | show | jobs | submit login

I got a piece of advice writing UI code a long time ago: Don't marry your display code to your business logic.

I'd like to say this has served me well. It's the reason I never went for JSX or other frameworks that put logical code into templates or things like that. That is one abstraction I found unhelpful.

However, I've come around to not taking that advice as literally as I used to. Looking back over 25 years of code, I can see a lot of times I tried to abstract away display code in ways that made it exceedingly difficult to detect why it only failed on certain pieces of data. Sometimes this was sheep shaving tightly bound code into generic routines, and sometimes it was planned that way. This is another type of abstraction that adds cognitive load: One where instead of writing wrappers for a specific use case, you try to generalize everything you write to account for all possible use cases in advance.

There's some sort of balance that has to be struck between these two poles. The older I get, though, the more I suspect that whatever balance I strike today I'll find unsatisfactory if I have to revisit the code in ten years.




I learned that lesson building an utility with JavaFX. I've done a few years with React and the usual pattern was to move almost everything out of the components. Unless it's an event handler, a data transformer for the view, or something that manipulates the view, it has no business belonging to this layer.

I don't try to generalize it, I just try to make the separation clear using functions/methods/classes. Instead of having the post button's handler directly send the request, I create a function inside the `api` module that does it. It does have the effect on putting names on code patterns inside the project.


When you say "components" do you mean that in the mixed React sense where a component could contain HTML? In my own usual cases, I call the Javascript a component, and the HTML a template. I usually take a handlebars approach on the template content and something like "data-role" to identify the template tags to the JS, and beyond that don't mix them. However, my client-facing JS components themselves are totally bound to the templates they load up - they expect the presence of certain fields to work with. I'm talking more about not mixing any business logic into those JS components: Let's say, in a form component, not anticipating that a dropdown menu will have any particular shape or size of dropdown item, which means those items need to be specified separately. This leads to JS components relying on lots of other components, when sometimes you just need one type of dropdown item for a particular component, and having dropdown items be a 20-headed beast makes everything upstream need to define them first.

Sometimes you just need a form to do what it says on the label.


> Don't marry your display code to your business logic.

> It's the reason I never went for JSX or other frameworks that put logical code into templates or things like that

That's not mixing business logic with display code, your confusing (or conflating) 2 entirely different things here.


I don't think I'm conflating things. Any time you insert sugar into your HTML that makes your end user see output that's been inserted by how your framework interprets that sugar, you may be binding your business logic to your display code. Possibly in subtle ways you don't realize until later. A common case is rounding decimals to a user that aren't rounded in the system.

Letting any control flow into your HTMX/JSX templates is begging for trouble. It boggles the mind that people abandoned PHP - where at least the output was static and checkable - for something exactly like mixed HTML/code where some processing was done on the front end and everything was supposed to be dehydrated. Only to pivot again to hydration on the back-end.

JSX and React came on the scene 10 years after I first realized using the client for anything logical was an anti-pattern. Back then, I remember people would write things like:

CustomerFormForClientX extends CustomerFormForClientZ [...]

and put a bunch of validation on the client side, too. Clients are dumb terminals. That's it. I'm not confusing the use of logic-in-JSX with business-logic-in-display. One only has to look at every basic JSX example on the web that's taught to newbies to see a million ways it can go wrong.


As someone who has little experience in this topic but has come to a similar conclusion, I think the main downside of this strict separation you're recommending is performance/efficiency. Have you noticed that to be a problem in practice? It's not always clear whether the simplest solution can actually be feasible, or perhaps that is just a reflection of still untapped understanding of the problem ___domain.


Generally there isn't a huge tradeoff in business software. In games where every CPU cycle counts it's another story. And yes, I have been guilty of writing display code divorced from game logic that was way beyond the scope of what needed to be actually displayed on the screen for a particular situation, and having that over-generalization lead to unacceptable performance drops. So you make a good point.


I think your principle is good, but this is going too far to throw out react.

You really just want to split those things out of the UI component and keep minimal code in the component file. But having it be code free is too much for real client side apps.

Modern apps have very interactive UI that needs code. Things like animation management(stop/start/cancel) etc… have subtle interactions. Canvas zoom and pan, canvas render layouts that relate to UI, etc… lots of timing and control code that is app state dependent and transient and belongs in components.

I apply the simple principle and move any app state management and business logic out of the component UI code. But still lean into logical code in UI.


I started with a templating system that had a very limited logic and I'm still quite fond of this approach.

Basically arguments for a template had a form of a tree prepared by the controller function. The template could only display values from the tree (possibly processed by some library function, for example date formatter), hide or show fragments of html dependaning on the presence or absence of some value or branch in the tree, descend into a branch of a tree and iterate over an array from the tree, while descending into one iteration at a time. Also could include subtemplate feeding it a branch of the tree. This was enough to build any UI out of components and kept their html simple, separate and 100% html. Even the template logic had a form of html comments. One could open such template in any html editor including visual ones. It was all before advent of client side frameworks.

You could mimic this in React by preparing all data in render method as a jsonlike tree before outputting any JSX tag and limit yourself inside JSX part to just if and map(it =>{}) and single value {}


Yeah. I did this too. Mine's driven off a database with a structure of

1. pages, each of which may or may not have a parent pageID; the entire tree is read and parsed to generate the menu system and links/sub-links

2. modules which reference HTML templates to load through a parser that replaces areas with {{handlebar}} variables written by the clients through their own API

3. something like page_modules which locate the modules on the page,

4. a renderer which uses the above stuff to make the menu system, figure out what URL people are trying to get to, load the page, load the modules in the page, and fill in the crap the client wrote inside the {{handlebars}}

This has worked so well for so long that I can basically spin up a website in 15 minutes for a new client, let them fill in anything I want them to by themselves, throw some art on it and call it a day.

It worked so well that I ended up writing a newer version that uses a bunch of Javascript to accomplish basically the same thing with smoother transitions in a single-page app that doesn't look or act like an SPA, but it was basically pointless.


Ahh yes, the labor of love that is code maintenance.

Done is better than perfect until you’re reviewing the code in 10 years (or maybe 3 years with a more adept set of eyes over your shoulders).

These days I ask my teams to be less clever and more simple.

Simple usually wins over clever in the long run.


Completely this. KISS, my favourite acro for this.


One implication of this would appear to be that syntactically significant leading whitespace, as in Python, is wrong.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: