Don't solve problems by building solutions

Don't solve problems by building solutions

Change the design or constraints to eliminate the problem.

This article is a bit different than my other ones, I'm excited to share something I've learned at Bobsled (the start-up I work at) from Andy (our CTO).

It has been fun working with Andy, I've learned a lot, not just directly from him, but a lot by observing him. I'd say he is very pragmatic, and he always asks questions that you didn't think of.

I think this is something you realize with engineers who are extremely experienced. They ask the important not-so-obvious questions, and I think this is one aspect where Andy shines.

I want to share something I learned from him when it comes to thinking about solving a problem.

Question

This tweet was tweeted out the same day I learned this from Andy. We had a problem to solve:

  • My idea was to build a solution
  • Andy's idea was to change the routing to completely eliminate the problem

This way of thinking really taught me that you shouldn't always try to build new solutions. Sometimes a better option is to change the existing software to completely eliminate the problems.

The nice part here is the reduced complexity. Instead of writing a bunch of new code, you change the existing software.

The best code is the one you've never written.

Story

Let's dig into the story.

At Bobsled we had a share page with the route /shares/:id. This is the page where you can view and edit a single share. The provider and consumer would both be able to view and edit things on this page. On the page, you can open multiple dialogs to change the contents of the share.

  • A share is the term for the sharing of data that gets done between a provider and consumer
  • A provider is the organization or business unit that shares data
  • A consumer is the organization or business unit that receives data

Both the provider and consumer can edit things on the share page, but not exactly the same things. They have different permissions for what they can change.

We used to display things differently on this page by constantly checking in the UI code whether the role of the user is a provider or consumer.

One thing to point out here is that the page just keeps growing in what you can do on it as time pass.

Since we're using Remix, we use nested routes for our dialogs, hence we have multiple nested routes since there are multiple dialogs on the share page.

  • For instance, if you want to edit the destination of a share, when you open the dialog, you're just going to the /shares/:id/destination route.

Image to help understand how the page looks like:

Screenshot from 2022-11-01 15-51-03.png

Things here that lead to increased complexity:

  • Having to retrieve the user's role in every route
  • In the UI code, depending on what we should display, for some elements we have to check whether the user is a provider or consumer

Let's look at how we solved this.

Solution

One solution is to have a single source when getting the role of the user by fetching it, returning it from the root of the app, and consuming it on the client in any routes through a hook (function) Remix offers called useMatches.

This was the solution I proposed, but we still have the second problem I mentioned:

In the UI code, depending on what we should display, for some elements we have to check whether the user is a provider or consumer.

Now, you may say this is quite common in React to conditionally render UI, but in our scenario, it was repetitive and made the code quite bloated due to the numerous cases we needed to check.

I didn't think of this as a problem, or if there was a way to change it. This is where Andy's idea comes into the story.

Since we know the share page will continue to grow, and should be visible and editable for both the provider and consumer with different permissions, let's separate the page depending on the role of the user.

  • The route for the provider's share page: /shares/:id/provider
  • The route for the consumer's share page: /shares/:id/consumer

This is super exciting!

By doing this we eliminate both problems we previously had. We immediately know because of the route whether it is the share page for the provider or consumer.

This really reduced the complexity of the share page.

  • We don't need to check for the role inside both routes like we did, including their nested routes, i.e. /shares/:id/provider/destination, /shares/:id/consumer/destination, etc.

Conclusion

Don't be naive.

Don't rush.

Don't be hasty.

Don't always try to build new solutions when you are facing a problem. Step back and think of other ways of solving a problem, including ways that don't require new code.

This reminds me of designing it twice from the book A Philosophy of Software Design. Basically, the idea is to think of multiple alternatives (even bad ones) before coming to a decision, so you can compare the alternatives. Often then it is easier to see which alternative is the better one. I wrote about it here: Design it twice.