22 August 2024
What's a design choice you often regret?
During our daily all-hands standup we typically pose a Question of the Day. Recently, I posed the question: What’s a design/architecture choice you look at and say “oooh, we’re gonna regret that”? We got a lot of interesting responses along with our project updates.
Design-wise, for me it is modals to display details that probably should belong in a “show” page. The amount of content we display will only ever grow, and at some point those modals are gonna get unwieldy. Just make the show page and accept that it might not have much stuff on it at first. It’s faster to do that than it is to go with a modal until you outgrow the modal and now you’re trying to re-home a bunch of things while making what should be a fairly small, additive change.
The other design one is “We’ll just use this UI toolkit” (e.g. a pre-made bootstrap theme or something like MaterialUI) … these work super well if you’re staying within the fairly narrow bounds of what they offer (and occasionally adding brand-new things), and really, really awful when you try to modify what’s already there.
(I don’t have this complaint about using Tailwind or other CSS frameworks that are fairly transparent abstractions of CSS, rather than pre-built sets of styled components)
Architecture-wise, role-based authorization in the view layer (to show/hide content).
<% if user.super_admin? || user.admin? || user.head_of_finance? || user.has_invoice_privileges? %>
<%= approve_invoice_button(@invoice) %>
<% else %>
<span>Sorry, you can't approve this</span>
<% end %>
The first line gets unwieldy fast, and then whenever roles change or you add new roles (or try to refine roles further), it starts to really, really get unpleasant. Plus there’s not a great way to say “Okay, what are all the things this user is allowed to do?”
It gets even worse if you have to share authorization logic with, say, an SPA front-end or mobile app.
Along the authorization front, trying to really hone in on RESTful stuff so that everything that updates a record flows through the same PUT/PATCH /some_resource/:id
route – there are lots and lots and lots of different business stories behind updating most types of non-trivial records, and they usually require different sets of rules.
I’d rather see something like:
PUT/PATCH /invoices/:id/approve
PUT/PATCH /invoices/:id/mark-as-paid
PUT/PATCH /invoices/:id/cancel
PUT/PATCH /invoices/:id/change-recipients
(or whatever makes sense in this business domain) Then each of those actions can be easily separated from the others.
This is actually one thing I’ve really, really liked about LiveView – lots of stateful actions end up with their own little event handlers, so you get natural separation.