Howdy, meat robot!

This is an archive of a DevChat newsletter. To get the next one early, and in your inbox, sign up! To read past issues, head to the archives!

Welcome to DevChat #5. Last week my what-did-you-like-most poll revealed that the stuff about ADHD was of the most interest to people. To those who responded, anyway. But that's the whole thing about feedback: you can only get what you want by making it known!

With that in mind, I won't be holding back on personal topics. While this newsletter is meant for deep dives into technical/business stuff in games and web development, being a person is the most complicated factor in all of that stuff anyway. Let's not pretend otherwise.

Today we've got:

  • Week 1.5 of ADHD meds
  • Making technical tools meant for other people
  • Typescript migration nightmares

ADHD meds: continued!

Picture of a pill bottle labeled as contained Ritalin.

Last week I wrote at length about my struggles with ADHD and depression, and reported that being switched onto new meds was already helping even after a few days. (Someone asked me on Twitter what, specifically, I got put on. Why not go all in on full disclosure? Ritalin, 10mg short-acting, twice per day.)

Here we are ~10 days in, and I can now confirm with confidence that my life is much better. As I noted earlier, the effects were not as profound or observable as I'd heard others experiencing. But that doesn't mean anything. We're all different, in need of different outcomes, and what matters is if you get what you need. Plus, it's hard for your brain to notice when it's working differently, since it's also the thing doing the noticing.

In short, the constant negative mental pressure I'd been feeling is dramatically reduced, enough so that while the meds are in effect I can do things I don't want to do without nearly as much mental anguish and, more importantly, I can do things I do want to do. If you haven't experienced ADHD or depression (there can be overlapping properties), the idea of wanting to do something and then simply... not doing it... is probably pretty weird. Apparently that isn't normal. Who knew?

Example: today I was able to do a few hours of chores, spend another few hours finally mounting sound dampening panels to my office wall, fill out a bunch of medical paperwork, send a bunch of administrative emails, and now sit down to work on this newsletter. Any one of those things prior to medication would have been an insurmountable struggle. I only started to feel that downward pressure after my first dose wore off. But hey, that's what the second dose is for.

This "dependency" on drugs to be able to accomplish things can be seen as a problem because of how our society sees mental health, and that fact often prevents people from even seeing a psychiatrist. After all, what if you do get put on drugs? Now they're a crutch, right? Isn't it a personal failing to need medication to feel okay?

No. It's not. It's just MEDICINE. Is it a personal failing to wear a cast when you break your arm? Or to wear glasses so you can see? Or to take drugs to keep from having a heart attack? Such claims would be obviously wrong to nearly everyone. It should be just as obviously wrong for psychiatric treatments.

The part that freaks people out even more is the idea that you're changing how your brain works, and maybe that means you aren't you anymore.

Here's the secret, though: you're just a weird biological robot. Your consciousness is a collection of computational subsystems all trying to accomplish various things in parallel, all the while pretending its a single entity. All experimental evidence indicates that this "you" is mostly a narrator trying to explain the actions of the rest of your brain, in retrospect.

Wild, right?

Even if you don't believe that the self is imaginary in the first place, you can't get around the fact that it's created and maintained by biological processes. Those are dictated by changes to chemical gradients in your brain. Your brain is awash in chemicals that dictate how it responds to its circumstances, and the balance of those chemicals is constantly changing based on what you do (and don't do). Why single out a medication as being the thing that makes you "someone else?"

Other things that also turn you into a different person, if you think that's what psychiatric medications do:

  • Eating.
  • Not eating.
  • Being hungry.
  • Being full.
  • Sleeping.
  • Excercising.
  • Thinking.
  • Socializing.
  • ... (you get it)

Making technical tools for other people

The Prettier logo, as shown on its website.

The past couple weeks I've been doing a lot of code review with Shi, our platform specialist. He's been working on developing a testing library for GameMaker Studio, so that we can have a much more test-driven gamedev process, and so that we can build some robust automated tests to evaluate new builds of the game engine.

These code reviews found us frequently getting into the philosophy of testing, documentation, and tools. In particular, we were constantly bumping up against the design challenges for this test library, since it was meant to be primarily used by Shi (the creator of it) and Seth (our game programmer, who would be using it but not digging into its code).

The main conflict was that the creator of the tool (Shi) and one of its primary users (Seth) had such different use cases that it was easy for Shi to preferentially design for his own use cases. There's nothing unexpected about that -- he was the one building the tool, after all!

Having spent most of my time at Bscotch building tools for other people to use, I had fallen into this trap on many occasions and could see its signs. It's still quite difficult to recognize the signs when I'm doing the work, though! Turns out code reviews and pair programming are pretty dang useful for staying out of trouble.

Anyway, when building tools for other people to use, here are the things I try to keep top of mind:

  • Design for the most-common use cases. You probably want to cover a wider swath of use cases than only the common and important ones, but those additional cases shouldn't come at the cost of good design for the main cases.
  • Know who is going to use it. Design for their experience first! We are probably going to open-source this test library in the future, but our internal team (especially Seth) are still the most important target audience. So while we want the library to be usable outside our context, that fact cannot be a design priority.
  • Make basic use dead-simple. Common use cases should be easy, and the tool should use reasonable defaults instead of forcing the user to do a lot of configuration work.
  • Don't make the user learn. The user should not have to know how the tool works in order to use it.
  • Minimize requirements. The tool should force as few changes/requirements/expectations as possible on its user, environment, and target.
  • Minimize functional limitations. Sure, you're designing for a particular range of use cases and you should not invest time in designing for use cases you won't deal with, but that doesn't mean you can't expose options that you might otherwise have kept private and static!

Let's look at a an example of all this done well: Prettier.

Prettier is a code formatter for front-end web code. It's built around the idea that what your code looks like is both extremely important, and not important at all. To clarify, it's important that code be formatted consistently and in a way that makes it readable, but the details don't matter.

Spaces or tabs? Two spaces or four? Newlines before left curlies? Semi-colons always, or only when required? While you may have opinions on all these questions, it's hard to argue that it truly matters. There's a reason that any particular formatting preference has as many die-hard proponents as opponents: it just doesn't matter.

So Prettier says: just code without ever even worrying about style, and let automations do the work. Arguments over formatting are a waste of time.

You could format-on-save, or with a git hook, or whatever. The point is that you aren't doing it, and you didn't have to make any decisions about how it should be done. Prettier defaults to a reasonable format that most people will be okay with, and therefore requires no configuration for your average use case. It doesn't require that you do, well, anything in particular. It'll just work on whatever front-end code you've got.

How about something done poorly? I submit to you, Conventional Changelog.

Conventional Changelog is a spectacular idea. In short, it's a set of conventions and tools for enforcing structure on your git commit messages, with the excellent outcome of being able to directly convert your git history into patchnotes/changelogs. I borrowed this idea for the patchnotes you see for our games, so that no one on the team had to do extra work to cause our patchnotes to exist.

The fact that I made my own version of this tooling tells you everything: Conventional Changelog is horrible tooling.

Yeah, I said it! So... what does it do wrong?

  • It has no common use cases. In fact, if you can figure out how to use it at all, or which tool to use, or what the default formatting expectations are, you'll be miles ahead of me!
  • It was apparently made generic enough for anyone, and has no obvious initial opinions on what it should do. So it sorta... does nothing by default. It has no target audience.
  • It's super hard to use out of the gate. I do use it on some of my open source projects. Here's the CLI command that makes it go: conventional-changelog -p angular -i CHANGELOG.md -s -r 0. I had to specify which formatting options I want ("angular"), but there's no list anywhere of what the options are. I also had to add those final -s -r 0 options. I have no idea what those do, or why they're required. I apparently figured all of that out a long time ago and have been copy-pasting ever since.
  • The amount of work you have to do to learn anything about what this is and how to use it is absurd, given how simple the main use case is (enforce a simple convention for commit messages, and create a Markdown changelog with those messages).
  • The one thing they have going for them is that they have truly minimized their functional limitations. The project has seemingly dozens of tools that do different things related to this problem, each with infinite options and the ability to add plugins etc. The worked so hard to have the tools do everything, that they don't do anything without a lot of work.

Be like Prettier, not like Conventional Changelog.

Typescript migration nightmares

Screenshot of a terminal showing several Typescript errors.

Last week I told you I was going to start converting Rumpus (our webtech stack, including our website and the server that supports our games and login system) from plain Node.js (basically JavaScript) into Typescript. I'm now a week in, and it's a FRIGGIN' NIGHTMARE.

(If you don't care about webdev in general, and Typescript in specific, you'll probably want to skip this section.)

Between tests, build/deploy scripts, and all the actual server code, Rumpus consists of nearly 600 JavaScript files. It's the first project I worked on using Node.js, and I'm a self-taught programmer in the first place. Which is to say: much of the foundational code is 2+ years old and bad, and there is a lot of code.

The goal with moving to Typescript is to make future development far easier by making it so my tools can do way more work for me. The tools can remember things so I don't have to, and they can see when I've written code that probably isn't doing what I expect.

Here's what the first week of conversion looked like:

  1. I reorganized the entire project to separate code files from non-code files. This is because Typescript has a compile step, and it is best to have the compile stuff appear somewhere else, which means that by default any path references to non-code files (like JSON or markdown) will be broken. Rumpus runs on Linux (I use Windows Subsystem for Linux to run it on Linux on Windows... and technically it's also in a Docker container on Linux...) so I was able to use the du utility to list all of the non-JavaScript files. Then it was just a matter of moving each one and checking it off the list.
  2. I had three, pseudo-independent sub-projects (tests, deployment code, and server code), so I used Typescript's "project references" to organize things to speed up Typescript compiling.
  3. I used AirBnB's "ts-migrate" tool to batch-convert everything from JavaScript to Typescript. The goal of this tool is to get things compiling as fast as possible, largely by ignoring Typescript errors. This turned out to be insufficient for my goals, but was still a good start.
    1. One of the commands you can use is the reignore command. This one is super useful for getting your project compiling. It goes through your project looking for Typescript errors, and then for every one it finds it adds a compiler directive comment (something like // @ts-expect error ...) so that Typescript just says, "Cool, I don't care that this line looks broken!" and spits out compiled code anyway. I've found myself frequently re-running this script to get things compiling, since sometimes that's needed to move forward even with broken code.
  4. Node.js uses "CommonJS" modules, which is a fancy way of saying that you use commands like const myModule = require('./myModule') to import code from one file into another. Typescript uses a totally different, more modern way. It looks like import myModule from "./myModule". There is a Typescript compiler option called esModuleInterop that, if set to true, allows you to use both at once. I did that, and ended up with valid Typescript, but I needed to change everything to the new syntax to get other stuff to work. So I had to do a bunch of regex magic to make the conversion. Things like ^const\s+(\{[a-z0-9]+\})\s+=\s+require\(([^)]+)\)\s*; and ^const\s+([a-z0-9]+)\s+=\s+require\([^)]+\)\s*; replaced with import $1 from $2;. There turned out to be a lot of nuance here, so there was a lot more to it than just this.
  5. Finally, I had been adding type information to Rumpus using JSDocs. JSDocs are a way of writing comments that can perform a lot of the duties of Typescript, but in a far more clunky and restricted way. Still a lot easier than converting a project to Typescript, and a lot better than nothing, so that's what I'd been doing. I wrote a script that would discover all of my JSDoc comments and then convert them into real Typescript injected into the associated code. The npm module comment-parser was extremely useful for this.

That already sounds like a lot, but it's only the barest of descriptions of the process so far. The end result was, technically, Typescript. However, having the project converted revealed some serious compatibility issues:

  • I use a super popular library, Express.js, for the server logic. I had extended it to add some convenience utilities, but apparently in a way that was quite incompatible with the typed version of the library. WHOOPS! I did a bunch of regexing to try to fix this, but that can only take me so far. Things are still quite broken.
  • Most of what the AirBnB converter does is to just tell the Typescript compiler to expect a jillion errors. So all those errors are still there. Every time I fix some, a bunch of those expected errors are no longer expected. This causes more errors, because now the // @ts-expect-error directives are flagging things that aren't errors, which is itself an error! Fixing ignored errors creates an error cascade, so it's sort of an all-or-nothing situation.
  • The language server and compiler are sloooowwwww. This is a known issue with Typescript, but apparently the "project references" organization approach can go a long way towards fixing it. I'll need to break the project apart more before getting everything fixed, because otherwise having to wait 5-10 seconds to get code completion and error detection makes it quite difficult to make the literally thousands of fixes that are now required.

So. How am I feeling now about all this?

The funny thing about treating my ADHD is that now I care about the work again. And now that I care about the work I worry about it. I've used Typescript enough in other projects that I know how valuable it is. And I know just how much it sucks that Rumpus isn't already in Typescript. The payoff is obvious, but holy crap, the cost.

More importantly, I can't shake the fear that I just won't be able to do it. That despite all my fancy regexing, and creation of meta-coding tools, and my strong knowledge of Typescript, I'll spend a few weeks only to discover that I've ended up somewhere so fundamentally broken that I'll have to throw all that work away.

All I can do at this point is trust my skill, and the fact that my team at Bscotch is giving me the space to make this priority (because they trust my judgement), and just assume I'll eventually figure it all out. So that's what I'm going to do. Or at least pretend to do. Fake it until you make it, right?

Anyway, I'd love to put together a multi-part, super-deep dive into this journey. But I don't know if people will find it interesting or valuable. So please let me know if that would be of interest!

Next time

That wraps DevChat #5!

Give me feedback and tell me your thoughts!

Share with others by forwarding or linking directly to the archived post.

Have a great week!

❤ Adam