00:00:00.000
foreign
00:00:09.679
Thanks, everyone for coming out to my talk, "Blank Page Panic: Creating Confidence through Test-Driven Development." My name is Elayne Juten, and I use she/her pronouns. I'm enjoying my second career as a software engineer at cars.com, located in Madison, Wisconsin. Shout out to Wisconsin! As they say, the Midwest is best.
00:00:13.980
I also have two adorable cats, Henry and Otto, whom I miss very much. Let's give a round of applause for cats! I love them.
00:00:24.060
So, blank page panic. It's in the title, but what the heck is it? It's a phrase that I started using to describe something along the lines of writer's block, but for coding. It's where I'll pick up a story card, read it through, and feel really comfortable, like I know what to do. I think, "I've got this!" I open up my editor, and then I see that cursor just staring at me.
00:00:35.640
I start to sweat a little bit, I get more nervous, and I look around. I work remotely, so I’ll look at my cats, and they know that I know nothing. Then I start to think that I know nothing, and I spiral. At my worst, I end up having a panic attack. In earlier years, before I realized there was a way to fix this for myself, I would really dive into that hate train and stay in hate station for a long time, achieving little to no work.
00:01:00.059
Doing some research into writer's block, since it is a similar phenomenon, I found a quote that really spoke to me from Gene Fowler, an American journalist. He said, "Writing's easy. All you need to do is stare at a blank sheet of paper until drops of blood form on your forehead." I feel like I've been there several times, but it does get easier. While researching writer's block, I noticed four core underlying causes that seem to come up the most frequently: fear, perfectionism, self-criticism, and external pressure. I definitely have the trifecta of fear, perfectionism, and self-criticism. They kind of live together in the same house.
00:01:30.240
On particularly good days, I create invisible external pressure to boost my self-esteem. On those days, it's really fun to be me! So before we delve into the technical aspects of what I found helps me overcome anxiety and panic, I like to take a non-technical route.
00:01:53.759
One of the first things I do is exercise or just have a change of scenery. I need to get away from the editor, so I’ll get up, make a cup of tea, or walk around the block. Even just getting a fresh cup of water helps calm me down a bit, allowing me to return without that sheer panic mode. Another helpful technique is switching tasks. If I catch myself spiraling into hate station, I grab a pull request, respond to some emails, and start checking off items from my to-do list, building up that confidence.
00:02:15.200
However, if I find myself deep in self-loathing, I know I need to step away. I can’t tackle a pull request when I’m hating myself; it’s just not fun. I like to couple this tip with putting myself on do not disturb on Slack and blocking off time on my calendar to let myself know that this is when I’m going to start working on this story. This minimizes distractions for me.
00:02:43.800
I don't have social media, but ironically, I find myself checking all the social media when I feel uncomfortable doing something. One thing that has really stuck with me over the past three years of putting this talk together is the phrase 'progress not perfection.' I’m definitely a perfectionist, and adopting the mindset that each step is progress toward the end goal really helps me. It doesn’t have to be pretty; eventually, we’ll get there.
00:03:05.700
Now, 'progress not perfection' is a great segue into test-driven development, or TDD, as I’ll likely call it throughout this talk. TDD is a process of writing tests first, then following those error messages to either a new error message or having the test pass. Before I write my test files and get everything going, I start with a lot of pseudocode. I love pseudocode! I write notes to myself all the time in my coding files, deleting them before I commit if they’re inappropriate. These notes usually consist of high-level descriptions of a product or process and are commented out so the program won't see them—only I can.
00:03:27.300
Let’s set the scene for this example of how we’re going to get out of this panic: a local humane society has an application called Creature Comforts that they use to track all animals from intake to adoption. They want to add a feature that calculates food for meal times so volunteers know how much to feed each animal. We receive this story card: as an adoption center employee, I want to have the daily amount of food calculated for an animal based on the animal type. For this instance, we’re going to do dogs and cats, and also based on their weight—and the threshold is 10 pounds or less.
00:03:55.760
The story card seems to make sense. I'm still not feeling great about myself, but I did get my cup of tea and fresh water, and I’m ready to go! So, this is where I go into the pseudocode setup. Again, I write a lot of pseudocode and use three forms of it, which I call the pseudocode dump, Focus pseudocode, and structured pseudocode. The pseudocode dump is basically copying and pasting the story card. I like to limit distractions, and if I don’t have to switch between a browser and an editor, it makes my life much easier.
00:04:21.600
I try to keep everything in one place so I can reference it when I want. Then I write the Focus pseudocode, which involves taking out a small piece of work that I’m going to do first. This makes it easier to handle in bite-sized pieces, similar to working on a PR or responding to an email. This is something easier to build up my confidence, and then I move on to structured pseudocode, where I brainstorm ideas for variable and method names. Naming things can be the hardest part of development, so I jot down my ideas knowing I can change them since it’s just pseudocode.
00:04:43.740
Here’s what my pseudocode dump looks like: within the daily routine, food needs to be calculated based on animal type and animal weight. Ten pounds is the weight threshold for both dogs and cats. In the Focus pseudocode, I’m stripping down the general story card to make it easier to work with. I'm saying that we need a method to calculate food in the daily routine file, starting with dogs over ten pounds. Once that passes, I'll add the dogs under ten pounds.
00:05:11.360
Then, in the structured pseudocode, I repeat my thoughts, which, at this point, I need to make sure I document clearly. Within the routine class, I’ll write a method to calculate food, indicating that I need to pass in the animal type and animal weight variables. For animals weighing over 10 pounds, they’ll receive two scoops of food, while under 10 pounds will get one scoop. I feel okay; I have something in a file, which is always a good sign!
00:05:42.900
Now, I’m going to move on to writing our tests. RSpec is a testing language written in Ruby for Ruby, designed with test-driven development in mind. There are several blocks we can use to flesh out our skeleton—no pun intended. These blocks include "describe," "context," and "it." I like to visualize these as an upside-down triangle, similar to the pseudocode I wrote, moving from general concepts to specific method names.
00:06:06.600
The "describe" block refers to a class or method and contains broad ideas. We then move into the "context" to add more details to the describe. For instance, the describe could be the calculate food method, while the context might specify when it's a dog weighing over 10 pounds. Finally, the "it" block lays out the specifications for that context, defining what outputs are expected.
00:06:40.560
Let’s set it up! I’ll start with the "describe" block. In my routine creator spec, I’ll describe the routine creator class. I’m creating an instance of the class to access the animal type and weight throughout our test file. The main thing I focus on is this "describe" block for the calculate food method to set that method call up.
00:07:11.360
Next, we move on to the "context" blocks. Within this section, I’ll say, when the animal type is a dog, and within that context, I’ll add another context for when the animal weighs over 10 pounds. This narrows our test case down, and I also create instance variables here to confirm that the animal type is indeed a dog and the weight is over 10 pounds.
00:07:36.800
So now we have our "it" blocks. For this one, it's pretty simple: it returns the proper amount of food. This is our skeleton for the test setup. I’m going to start running that test, and naturally, it fails. This is good! This is test-driven development; it’s hard to see red, but as long as we’re progressing and encountering new error messages, we’re on the right track. We haven’t required our routine creator test file yet, and we also don’t have the Ruby file.
00:08:04.640
But again, I want to tackle the simplest things first and just follow the error messages to figure out what I need to do. I’ll require the routine creator Ruby file in my test file and run the test again, predicting that it will fail. And it does! We don’t have that routine creator file, but slowly, I’m building up my confidence.
00:08:35.460
I like to predict whether the test will pass or fail to boost my knowledge. I’ll create a routine creator file, not adding anything else just yet. Again, I’m strictly adhering to TDD—doing as little code as possible to get to the next error message. Running that test reveals a failure due to an uninitialized constant 'RoutineCreator,' since the class was never created.
00:09:05.400
I’ll create that routine creator class and run the tests again, only to get an argument error because the test expects two variables, but I provided none. Sometimes it’s challenging to review all the failures; however, these are new failures, and no repetition exists. Looking back at my test case, I notice I created the instance of routine creator and included animal type and weight, so I should add those in my Ruby file.
00:09:36.840
I’ll create an initialize constructor to provide those necessary arguments and run the test again. The tests pass; the skeleton is set! I can feel my confidence slowly building with each step. Wouldn't it be great if my cats applauded each time I got a test to pass? Next, it’s time to move on to the actual test case. We have our skeleton, which is making progress and acknowledging what it requires.
00:10:09.900
Now, this is where "expect" comes in. While technically not a block, in the upside-down triangle, it's the pivotal point—it’s what you want the outcome to be. Within the "it" block, we’ll expect that the routine creator class's calculate food method equals two scoops of dog food. Running the tests shows they fail, but we had them passing before, which is fine.
00:10:45.120
The error states we have an undefined method "calculate food." That makes sense; I forgot to add the calculate food method in the Ruby file, so I’ll add it now. I will run the test again, expecting it to fail, and indeed, it shows that we are expecting two scoops of dog food, but it returns nothing because there’s no content in the method.
00:11:12.300
I'm feeling positive; everything is running smoothly, and I'm making progress. I'm able to navigate the test file, getting it through to the 'expect' portion, which is encouraging since that doesn’t always happen. I’m going to add a return statement to indicate two scoops of dog food. Though we’ll eventually deal with cats and dogs under ten pounds, for now, this is all we need.
00:11:36.600
I’ll run the test, and it passes again. We’re making progress! I’ll check my focus pseudocode and confirm we’re halfway through the dog section. The last step is to add dogs under ten pounds, which get one scoop of food. I'll add another context for when the weight is under ten pounds and run through the same process to confirm that the animal's weight is properly recognized, adding an 'it' block to represent this.
00:12:06.640
The process flows; I’ll run the tests again to validate that the system recognizes this accurately, though I know it might fail simply due to how I’ve coded it in the Ruby file. And indeed, it fails. We expected one scoop of dog food but got two scoops instead. As a response, I’ll simplify my coding practices. I’ll go for a minimal else-if approach so that if the animal weighs ten pounds or more, they’ll get two scoops; otherwise, it will return one scoop.
00:12:36.140
I’ll run the tests again, and this time they pass. I now have committable code, and it feels significant to start the process somewhere. I can reflect on my focus pseudocode and recognize that it’s complete. I’ve moved past the panic phase and can feel my confidence slowly growing with each test passing.
00:13:05.830
Now, looking back at my story card, there are still cats to tackle. I'm going to rewrite the Focus pseudocode. This will help me avoid getting overwhelmed by the bigger picture and instead allow me to chunk things down effectively. The focus pseudocode now includes animal type as we prep to add in the cats.
00:13:32.600
I know I’ll deal with cats weighing over ten pounds to receive two scoops followed by adding cats under ten pounds once that section works properly. This will likely mirror dogs and move simply through the same expected patterns. I will create a context for cats and what the appropriate amount of food should be in the test.
00:14:01.360
Now, I am going to run that test, but it fails because we need to ensure that cats over ten pounds provide the correct response of two scoops. That's fine—my immediate plan is to add the logic behind it to validate this test. I will add another checking statement in here to ensure that, if the cat exceeds ten pounds, the test runs affirmative, providing those two scoops of cat food.
00:14:36.780
I’ll run the tests once again, but they fail because we haven’t added any condition for weighing fewer than ten pounds yet. So I'll change my approach, tapping into my emotional self. Feeling confident, I think I know where this story is going, which is why I’ll start to refactor and open up my methods a bit more, checking to see if I can interpolate the animal type into the return string.
00:15:06.800
I’ll test this interpolation alongside creating an additional test since I haven’t evaluated the scenario for when cats are under ten pounds. We’ll want to ensure I’ve got all four test circumstances accounted for. I’ll run the tests once again.
00:15:39.420
And the tests pass! Thank goodness! I’ve never gotten applause in my test runs! Our second focus pseudocode for the story is now done. I feel great! We have successfully modified the routine creator spec file, having four tests verifying dogs' and cats' food amounts over and under ten pounds, and they all pass!
00:16:02.340
The final result is nice, clean, simple, and easy to read (at least to me). We can always refactor later; I can submit it to the PR gods for improvement. However, it’s the methodology I discovered that aids in overcoming those deep moments of doubt when one feels unable, like they can’t program at all when people seem to inherently know more.
00:16:29.040
This systematic approach has genuinely assisted me in escaping those low points, and I’m keen to know what strategies might work for any of you out there! Please feel free to reach out via Slack—I love gathering ideas. I think of this process as an a la carte menu, similar to test-driven development, allowing you to select what works best for you while discarding what doesn’t suit your needs.
00:16:54.320
It is an ever-evolving routine to extricate myself from bad situations. Thank you for your references. Once again, I’m Elayne Juten. Thank you so much for attending my talk!