Pseudo Code Notes

Ryan Florence -

I'm in the middle of a feature change in Remix. Previously we required you to return a redirect from any action. We designed the API that way for two reasons:

  1. To help developers avoid the bugs that pop up and then require an alert like "don't click back or you will be charged twice!". The way to avoid this altogether is to do POST -> Redirect -> GET, that way the browser no longer has the POST in the history stack to even click "back" to and resubmit.
  2. Because we were lazy and didn't want to have to track what you posted when the user clicks back, redirecting let us just forget about it.

But some recent features that are more important required us to change this behavior and let actions return anything, not just redirects.

Anyway, that's not what this post is about. This post is about how to handle making changes to difficult code.

The code that handles all of this is some of the most complicated code in Remix, and probably our least favorite stuff to look at. Naturally, it all lives inside a useEffect 😂. Long term, it could use some refactoring, but making tradeoffs is what software is all about, so I'm just going to pile some more complexity in for now and let our new hires refactor it later like every good founder does 🙈.

I like to look at the code and then outline what is happening in a bulleted list of sort of pseudo code but also notes. The point is to be able to "zoom out" of the actual code (that's like 200 lines) and condense it into something I can process. Here's my list right now:

- match routes
- setup redirect handling
- setup action error
- setup cDC emulator
- check if form is pending
  - call action
  - if error
    - put on emulator
    - track action error
  - else
    - redirect
    - return
- figure out matches to load
- call route transition deps
  - data, route module in parallel
  - skip if won't render
    - redirect
    - error
    - no route module
  - maybe preload blocking links
- if action errored
  - add to emulator
- transition results loop
  - if action errored && is exception
    - break
  - if not response or is error
    - or emulator has error already
      - continue
  - track emulator loader boundary
  - if error
    - add to emulator
  - else if redirect
    - maybe handle redirect
- extract new route data
- create next route data
- if still current and no redirect
  - if form is redirect/pendingget/action errored
    - reset form state to idle
  - setState

The actual code is 243 lines long but my brain can process 39 lines much more easily!

Of course, the ultimate goal is for the code to read as nicely as the list here, but sometimes that's a pipe dream--do your best but perfection will kill your ability to ship anything at all. I wish I know where I heard this quote so I could cite the author, but it goes something like this:

There are two types of software companies, those that go bankrupt and those that ship code that embarrasses their engineers

Now that I have this list I can think about what changes need to be made to the code to implement the features. This is where real programming happens for me: thinking. This list is designed to help me optimize my ability to think about these changes without getting distracted by the details of the actual code.

While looking at the list and thinking about the changes, I start making "todo" items in the list for my changes where I expect they'll go. My current list looks like this:

- match routes
- setup redirect handling
- setup action error
- setup cDC emulator
- check if form is pending
  - call action
    - [ ] don't require redirect
    - [ ] keep track of action result
    - [ ] put formdata on location state
  - if error
    - put on emulator
    - track action error
  - [ ] else if redirect
    - redirect
    - return
- figure out matches to load
- call route transition deps
  - data, route module in parallel
  - skip if won't render
    - redirect
    - error
    - no route module
  - maybe preload blocking links
- if action errored
  - add to emulator
- transition results loop
  - if action errored && is exception
    - break
  - if not response or is error
    - or emulator has error already
      - continue
  - track emulator loader boundary
  - if error
    - add to emulator
  - else if is redirect
    - maybe handle redirect
- extract new route data
- [ ] extract action data
- create next route data
- if still current and no redirect
  - if form is redirect/pending get/action errored
    - reset form state to idle
  - setState
    - [ ] include action data

Yesterday I finished creating the todo items and quit working for the day. This morning I opened my editor and then just followed my todos, getting the feature working in a few minutes 🎉

Maybe this can help you, too. Good luck!


Hi, I'm Ryan! I've been a webmaster since that was a thing. You might know me from React Router, Remix and Reach UI. You can follow me on Twitter, GitHub and YouTube.