on debugging trans identity
type Lyric
time 12 minutes
tl;dr This is a more lyrical post, a departure from what I’ve been blogging lately. Queue the music, kick back, and enjoy.
I’ve always been drawn into mazes.
I’m all too fascinated by the architects of puzzles.
I like to think of myself as a puzzle-maestra of sorts. Maybe all programmers are: we build codebases to solve for specific, near-invisible problems. Debug, debug.
I used to draw a lot of mazes. I filled my notebook with paths and fake-outs.
In the third grade, my friend’s little sibling said they wanted to grow up to be a boy. At the time, that was possible in my mind. When did I started cementing the walls?
It’s helpful to build boundaries in your application, to separate code by concern. Functions that interact with design over here. Functions that build infrastructure over here. Neat and tidy.
But when code throws an error, you have to root through your whole filesystem to find it.
How do you know where to start?
Error messages are, on the whole, shitty. Meaningless when they reference something beyond your knowledge. Outside the walls of your worldview.
The New York Times crossword puzzles are 15 by 15 squares. Everything neat. Orderly. On time. I love the crossword for its aha moments. The way that each solve hints the next.
Clever, really—designing a system where one conclusion leads so logically onwards.
I too often wish learning in the real world were more like crosswords.
But I tend to prefer mazes.
When I set out to solve a puzzle in code, I always forget I have a starting point.
The problem begins to overwhelm me. I can’t trace where in the stacks of lines it might be echoing out from.
Throughout my adolescence, throughout my college years, throughout my twenties, I was suicidal.
My brain craved escape from the maze.
A tangled bug in my code.
In crosswords, every clue is numbered, down or up. Each one gets a hint. When you’re stuck, you move across the puzzle to another clue—there’s always something to solve, always somewhere to work.
It’s clear how each solve contributes to the whole.
In a maze, you often have to retrace your steps. Maybe it’s more frustrating, especially if you’re using pen, but I see it as more true to life. Retracing still contributes to the solve.
Seeing progress while building an application is much harder. Bugs can cause regression. You might delete hundreds of lines of code trying to isolate the problem.
Sometimes working backwards is the only way through. Often, in mazes, starting from the finish is best.
There’s a tool—one of the first you learn in JavaScript—called console.log()
.
console.log()
displays for you anything you place between the parentheses.
console.log("Help me. I can't get out.")
The result, of of course:
Help me. I can't get out.
At first this talking with self doesn’t feel useful.
console.log("Hi, there.")
Hi, there.
Hi, computer.
You’re the one building the program. You should know what it might say. But sometimes, you run your application and:
Error. Gibberish.
Remember—you’re not coding in a vacuum. The browser is a player, too. You must satisfy something beyond yourself.
So, you Google the gibberish. Everyone on Stack Overflow is especially unhelpful today. No one responds to your Discord thread. You even Bing! it because you heard ChatGPT and Bing! got hitched, but first you have to join their early access program and—
You just want the damn thing to work.
Your brain hurts.
Maybe your body, too.
So, you figure out that the error occurs in one of three functions (if you’re lucky). You add a console.log()
to each function:
function one() {
// do stuff
console.log("Function 1.")
}
function two() {
// do stuff
console.log("Function 2.")
}
function three() {
// do stuff
console.log("Function 3.")
}
You run the code again:
Function 1.
Function 2.
Error. Gibberish.
You’ve learned something valuable now. The error happens in function three()
. Sometimes, this alone can be enough to figure it out. Other times, three()
drops into a rabbit hole. And warrens into more warrens.
Professional solvers can knock out a Sunday crossword within seven minutes. The crossword doesn’t lead to another puzzle. It’s one and done. It gets ever smaller.
Even in a maze, there’s only so many paths to trace. The finishing place is known. It’s just about getting to it.
In programming, complexity grows with the size of an application. For instance, there’s variables. If you say:
let age = 22
console.log(age)
You get:
22
age
becomes shorthand for the number assigned to it.
You can even come back to the code a year later and add:
age = 23
console.log(age)
And this time you’ll get:
22
23
age
, see, is variable. It changes with the passage of time—or, in this case, code. JavaScript always executes from top to bottom. From the beginning of its life to its end.
There are exceptions, but we’ll get to that.
Much is variable in code. It can get rather confusing. What, for example, does home
equal after this?
function moveOut() {
let home = "a place"
if (age < 18) {
home = "Ohio";
} else if (age <= 26) {
home = "Indiana";
} else if (age < 28) {
home = "Egypt";
} else if (age <= 29) {
home = "England";
}
return home;
}
console.log(moveOut)
“Well,” you may say if you’re clever, “home
depends on age
, but age
is never mentioned in this code, so we’ll get an error.”
For which you’d be technically correct and thereby have earned yourself a gold star.
But in this example, you can imagine that the value of age
is coming out of some calculation elsewhere. Now, the value of home is dependent not just on age
but on the calculation age
goes through.
What if the calculation is dependent on two other variables? Or if happiness
is dependent on home
? Or gender
dependent on age
in a separate file? Suddenly, we’re in a rabbit hole if an error gets thrown. Too many moving pieces.
Too many years going by in confusion.
Enigmatologists take much longer to create a puzzle than solve it. Here, the world is infinite. The boxes don’t yet exist to be filled. Where do you start?
Will Shortz, founder of the New York Times crossword, says it’s easy: “Most daily newspaper crosswords are 15 by 15 squares. So, [for example], I had three 15-letter answers. Planet of the Apes, monkeying around and Sir Edward Gibbon.”
Themes. Construct a helpful narrative.
One helpful tool for programmers is rubber ducking. Sounds more sexy than it is. Basically, when you run into your very last wit, you grab the yellow rubber duck you’ve set on your desk especially for this moment. Cradle him in your hands. You try to explain the bug to him in words only rubber ducks can understand.
This act of translation often causes us to frame the bug as part of a story. A theme.
In therapy I often tried to reframe my life narrative. I hoped that forcing it to all make sense for another human might give me insight into something outside the walls of my knowledge.
The problem and the beauty with rubber ducks is they can only respond with a squeak.
Incidentally, the Times pays $750 for a puzzle—$2250 if it’s a weekend. I couldn’t help but notice both numbers are divisible by 15.
Even in code, I try to make meaning. Arbitrary structure.
Instead of writing let
before defining a variable, you can write const
for “constant.”
Constants cannot be changed. const
as in constipated. Once there’s a value put in, nothing’s coming out.
const age = 26
We can zoom into a moment in time. Stay there for a while. Stop the years from going by.
One constant, however tentative, can often lead to the creation of another. If age
is always 26
, then the calculations dependent on it might be constant, too.
If a constant like brain
isn’t changing, maybe neither is heart
.
Creating a puzzle is more like solving one than you might think.
Jonathan Blow is one of my favorite enigmatologists. He’s best known, perhaps, as a video game designer, but I think of him as equal parts programmer and philosopher.
He’s also known for having a truly unfortunate last name.
In The Witness, Blow’s magnum opus, you’re tasked with exploring a vividly colored island through solving puzzles that at first feel as simple as moving a line from one side of the screen to another.
Throughout the game, these mazes grow in complexity, echoing their paths in the game world. In the picture above, for example, you may find the maze in yellow to mirror the paths of the cords on the ground.
”If I were to say anything about reality, it’s that things influence and interrelate and cause increasing situational complexity,” says Blow in an interview for Time. “You think that you have one or two or three simple things, and then you find that the way those interact creates new phenomena or sub-phenomena, and then those phenomena interact with the original things, and eventually you have a quite complex tapestry.”
In fact there’s a puzzle early in The Witness with a shadow cast on it from a nearby outcropping. The challenge is to draw a line from one side of the puzzle to the other, but there are many ways to trace the line. You may, like me, struggle until you realize: if you draw a line that perfectly separates the shadow from the light, it’s solved.
It’s a brilliant twist on puzzles, which when created by humans, are usually all too isolated from each other. It’s a game more about observing than solving.
console.log()
is, at the end of the day, just one of the many tools we have to observe code.
I like to think the things we learn in digital can transcend to real life.
In every video game where I was given the choice, I chose to play as a female protagonist.
In The Witness, you never get to inhabit a body besides your own. The first-person camera never interrupts the flow between you and the world.
A world of puzzles, which are certainly more than puzzles. Some enigmas take on real-life beauty.
Throughout The Witness’s 40+ hours, you begin to relate to its creator. Not to Blow the puzzlemaster, but to Jonathan the human. You stop solving puzzles long enough to start solving him.
It’s an indescribably intimate experience. Walking away from the puzzles only to find their creator.
In addition to console.log()
, programmers also have walking away as a tool for debugging.
It sounds counter-intuitive, but I can’t tell you how many times I get funneled into the wrong details. Something in the world can jog my mind, can inspire me toward a solve.
When I was in grad school, I loved the schoolwork but hated the teaching. Something about standing in front of students exhausted me to my core. Felt like a fake pathway. I’d put on a button-up and try to lead discussion, all while wondering what could be wrong.
I thought: I’m introverted. I thought: I’m bad at public speaking.
I was too close to the problem.
I rubber ducked with my therapist. I changed my routine. I tried everything.
How do we solve when the solution is invisible? When the problem is invisible?
Retrace, retrace.
I dropped out of grad school. I told people it was the workload that got to me, when, in reality, it was the putting my body into space each day.
I retreated into myself, tried to find the missing piece.
Maybe the most interesting part of The Witness is this: when you get stuck on all the puzzles around you, when you’ve decided to give up, you find yourself in the only place left that you haven’t been pulling your hair out over: the game’s earliest areas.
You reminisce over all the solved puzzles around you, over the time when you knew all the answers.
And in those environments, so often Jonathan has left something peculiar—something that stands out to you now. Something that directly helps with those puzzles you’re stuck on.
We sleep, we dream, we time travel, we wake, we walk, we solve.
I find it interesting that Blow’s first game, Braid, required the player to strategically rewind time to solve each level.
We’ve already talked about how variable code can grow in complexity, but we haven’t touched on asynchronous code.
In JavaScript, we have promises, often denoted by the keywords async
and await
.
For example:
async function activateCrosswalk() {
const ready = await signalChange();
return ready;
}
The code hangs when it sees that await
keyword, letting the signalChange()
resolve before telling the rest of the codebase that we’re ready to cross the street.
The thing is, a thousand cars can pass by during that waiting period. A bird can shit on a lamppost. Someone else (surely not you) might dart across the street despite the red light.
Asynchronous code is hard to monitor exactly because it makes things happen out of order. As I’ve mentioned, JavaScript normally executes right down the page, top-to-bottom, calling out to other functions on other pages when meeting them. It’s tidy, orderly.
It doesn’t dart across streets when the light is red.
When you ask the code to wait for a promise to be resolved, the rest of the world keeps moving while this one portion stays frozen in time.
When I was young, I thought, I’ll look like her when I grow up.
A lifetime can go by waiting for promises to be fulfilled.
A million points of entropy, a million paths in the maze, can spawn.
async
/ await
code can, of course, return a failure. In fact, asynchronous code is often used to avoid breaking an otherwise synchronous application. If code always runs from top-to-bottom, then any interruption means that the code must stop executing.
I’ll wait for this thing, and if it happens, I’ll do this. But if it doesn’t happen, I’ll do this instead.
Promises broken as easily as resolved.
A function that doesn’t return a value is called void
. And they can be the hardest to debug. An asynchronicity, then, can account for entropy in an otherwise orderly system.
It’s far too easy to forget the promises that we made ourselves. To forget what it is we’re waiting for.
I mentioned I was suicidal at one point. I never really gave myself an ultimatum; I just let my brain hang. There was no if this then live, else then bye. Always the general feeling of not wanting to exist.
I fundamentally didn’t know what I was waiting to hear back from the void.
When I was a kid, I drew mazes for my sister. They started, maybe, as doodles in church, but soon I found the need to increase their complexity, adding colored portals to other pages of my notebook. In letting the maze be gargantuan, I didn’t need a solution. I built the maze just to build it.
There’s a way queer children, especially those of us who grow up in abusive environments, manifest out of thin air garments to cover who we really are. Layers and layers. Fake pathways in a maze.
Jonathan Blow would be disappointed in such a puzzle—one designed not just to be difficult, but actually impossible to solve.
The point of a puzzle is, after all, to solve it. A good puzzle stumps you for a bit, asks you to walk away perhaps and come back, but it yields itself eventually.
The kind of maze I’d wrapped myself in wasn’t one that was going to be solved, but rather one that needed retraced.
I needed to walk away and find the creator.
The best way to solve a maze is to start at the finish and move to the start.
It was just me sitting in the middle with the minotaur, cleaning his axe.
Or, rather, I’d become a minotaur of sorts.
A good game, for me, is a fantasy. A way to dress up as someone I’m not.
Perhaps unsurprisingly, one of the best ways to debug asynchronous code—code knotted around and around itself—is with that simple console.log()
. You need more of them, sure, but a carefully placed console.log()
can yield a lot of information.
It tells you in what order things are happening. The asynchronous code could be reaching out to other websites for information, and those actions take variable amounts of time for each run of the code.
It’s that talking to yourself, observing carefully the order in which things occur:
wore a suit
unhappy
got called pretty
happy
My life and my fantasy were swapped, see.
In my fantasy world, I dressed like a boy. I kept my hair short. I tried, more than anything, to be gentle, and, more than anything, failed.
There was a walking away.
But there was also a promise returned.
To find the girl penning the maze.
A moment of solving for a variable, like name
, so dependent on everything else.
A simple matter of filling in the final, 5-letter clue:
alice