journal // May 17, 2024

What I Built This Week: May 13th-17th, 2024

What I Built This Week: May 13th-17th, 2024

This is the first post in a new series announced here. I’ll be tossing these posts in the “journal” category over on the sidebar. Enjoy!

This Week’s Background Noise

Keep Software Weird (2012) - A talk by Leon Gersing that I used to watch constantly back in the day. It came to mind this week and I’m glad it did—reminded me of a different (better?) time in software.

George Hotz on Lex Fridman Podcast - Big fan of George and his style and this is a great interview to throw on in the background.

What I Built for CheatCode

Joystick

I managed to solve two big issues inside of Joystick unexpectedly.

One was a dumb problem. In order to keep data-fetching stable (i.e., avoid parsing errors when your UI mounts on the client), I Base64 encode the data you fetch on your components (on the server) before sending it to the client.

This requires a decode step on the client. The problem? When I originally implemented the decoding step I was decoding all of the data for all components every time any component re-rendered (shockingly, this only slowed down renders by ~200-300ms). While working on stuff this week, I noticed that pages with a lot of data on them were slow to render. After a bit of sleuthing, I noticed the “duh” spot.

The fix was simple: centralize the decoding to when your UI mounts for the first time and then do incremental decoding when you refetch data.

The other problem that got solved was handling DOM changes by third-party libraries. If you’ve worked with React, it’s likely you’ve heard something to the effect of “never use jQuery or anything that modifies the DOM with React.” The reason why you’re not supposed to do that is that, if the DOM is modified independent of your framework (Joystick, React, Vue, etc), its virtual DOM—the current “snapshot” of your DOM in memory that makes diffing between changes fast/easy—doesn’t know about the changes.

I was implementing drag-and-drop using SortableJS the other day and after fighting with DOM elements randomly disappearing, I had an idea. What if I just tell the virtual DOM that that the actual DOM changed and have it update itself? Yes!

The end result: I was able to create a small, simple function that gets passed along with the component instance called sync_dom_to_vdom() that can be called manually to communicate DOM changes.

So, if your third-party DOM library has an event handler like “on after change,” in that event handler, you can just call sync_dom_to_vdom() and Joystick will update its VDOM automatically. The result? You can modify the DOM to your heart’s content without breaking future re-renders of your component!

The plan for these changes and some other stuff is to roll out an RC2 over the next week or two, but I need to do some more testing first.

FeatureComplete

To put Joystick and Mod through the paces, I’ve been working on a new SaaS.

It’s an app to help product teams manage their work and share progress with their customers/audience. The idea came about when I struggled to answer the perennial “when will this be done” question.

What I realized is that at the feature level it’s hard to predict when something will be done. But, if you break that feature up into the steps required to implement it, predicting an ETA is a lot easier. This is how FeatureComplete works. You organize work on your product into iterations (e.g., a batch of features/fixes for the month, a new version number, or an event) and each iteration is split into requirements. For example, I may have an iteration like Q2 2024 or 1.3.0 or Public Beta Launch.

Requirements are split into tasks and issues. Tasks are what you need to do to actually implement the feature, issues are problems with the current state of that implementation. Both tasks and issues can have an ETA assigned in minutes or hours. Behind the scenes, FeatureComplete totals up these ETAs for each requirement, and then for each iteration. The end result: you get a hyper-accurate estimate of how long an iteration will take to ship.

This week, my focus was on getting the requirement UI wired up to the back-end. A little demo:

The drag-and-drop feature on the tasks/issues lists served as inspiration for the sync_dom_to_vdom() function I hinted at above. I also wired up user assignment using the tag input component from Mod and got the discussion workflow working.

I’m not entirely sure what I’ll focus on next, but this UI got a big chunk of work out of the way. I think I’ll do some of the simpler list views next and then move on to the payments stuff (I want to design some components in Mod before I do).

Mod

Finally got back to work on Mod after a couple of distracted weeks moving to a new house. This week was focused on the JS side of things. I realized that the way I initially implemented modals and dialogs made it difficult to integrate those components into any JS framework (the JS I wrote was taking too much control over the behavior of the DOM nodes).

Previously, I was modifying the DOM in order to save the amount of HTML you had to add to your app. This worked, but it created another problem where the DOM manipulation in Mod would fight with your framework. Instead, I stripped it back so that you just provide the HTML and then the JS just attaches event handlers to that without modifying the DOM.

Like I hinted at above, I think the next move for Mod is to start adding some of the subscription-related components so I can test those out in FeatureComplete.

Clients

Continuing to flesh out the public-facing pages for the redesign of Moumint. This week, I nailed down the public profile which features an activity feed, list of collections, and list of drops (as well as follower information) that users have released on the site (Moumint empowers creators to connect and earn with their audience by offering digital art collections).

Profile

I also implemented the “follow” feature. Behind the scenes, I’m using MongoDB for data and ended up using a flat array of user_ids on each user to track who was following them. This makes it relatively cheap to follow/unfollow (I’m just doing an $addToSet or $pull query in MongoDB), but also, makes building back “who’s following you pages” even easier. With this approach, I can just loop over the array of user IDs and grab their avatar/username quick. Pop an index on the queried fields and boom: fast.

Next Week

I need to focus more on client stuff next week to catch up on lost time during the recent move. I’ll likely get in some CheatCode time later in the evenings—going to play it by ear.

What are you working on this week? Send me an email.

Written By
Ryan Glover

Ryan Glover

CEO/CTO @ CheatCode