00:00:08.790
Hi everyone! Thanks so much for clicking on my talk, 'Blank Page Panic! Creating Confidence with Test Driven Development.' In this talk, I will discuss the combination of pseudocode, test-driven development, and the RSpec testing framework, and how these three elements help me overcome that initial panic when picking up a new story.
00:00:14.350
A little introduction to me: my name is Elayne Juten. I'm an early-career developer from Madison, Wisconsin. I've been developing professionally for about two years now, and I work as a software engineer at Getty Images here in Madison. Also, I have two adorable cats named Henry and Otto.
00:00:28.660
So, what is 'blank page panic'? It’s in the title, but I sort of made up this phrase. For me, it describes the feeling I get when I pick up a new story. I read through it and initially feel confident about what needs to be done.
00:00:41.469
Then I open up a new file and stare at the blinking cursor, which starts to make me sweat a little bit. I glance around at my co-workers and wonder if they can sense that I don’t know what I’m doing. Thoughts start going through my mind: I just need to get some text on this page, but I have nothing. I spiral into self-doubt—how did I even get this job? I'm getting paid for this, but I shouldn’t be, and I never seem to make progress when I find myself in these head spaces.
00:01:05.110
To me, blank page panic is the programming equivalent of writer's block. I found this quote from Gene Fowler, an American journalist and author from the 1930s, that really resonates with me. He says, 'Writing is easy; all you do is stare at a blank sheet of paper until drops of blood form on your forehead.' It’s a little morbid, but I agree with this quote a lot.
00:01:19.200
Some underlying causes of blank page panic might include fear, perfectionism, self-criticism, and external pressure. Personally, for me, it’s the trifecta of fear, perfectionism, and self-criticism that coalesces into anxiety. That’s when I enter the negative headspace and find myself making less progress than if I had just started with something small.
00:01:41.630
Some possible ways to help get over this panic feeling are similar to overcoming writer’s block. The first one is exercise. It might be a weird time right now, but if you can safely walk around the block or just get up from your desk, it can really help reset your mind. Taking a couple of minutes to cool off, especially when you find yourself in that negative headspace, and then coming back with a fresh perspective can be very beneficial.
00:02:00.890
Switching tasks can also be effective. If you have a peer review to complete or if there’s an email to send, get those small, easy wins out of the way. This builds confidence and allows you to step away from the initial problem, coming back to it with fresh eyes. Changing your scenery can also help; if you can, move to a different location, like the kitchen table or a couch. Even just stepping away from your computer can help you return with refreshed vision.
00:02:29.810
Setting your devices to 'Do Not Disturb' and closing your email or Slack can also assist in reducing distractions. You can create a calendar reminder saying, for example, 'From 1 to 3 p.m., I can't be reached.' This way, all that email can wait. The distractions might just contribute to that panic feeling. And a strategy that has really stuck with me, which is the catalyst of this talk, is the mantra: 'Progress, not perfection.' As a perfectionist, I often remind myself that any step, no matter how small, is still a step towards the finish line.
00:03:00.890
Perfection almost always leads me to inaction. If I try to make something perfect, I end up not committing that code or turning in the assignment because it’ll never meet my standards. It’s crucial to remember that it’s about the progress you’re making.
00:03:25.560
This leads into test-driven development (TDD), which I see as a process of writing tests first and then addressing the error messages that those tests produce, filling in the code needed to pass those tests. For me, TDD perfectly embodies the 'progress, not perfection' model. By starting with tests, it sets me on a path for success.
00:03:43.690
So, when I am in panic mode, my first decision is to use test-driven development. The first step I take is to write pseudocode. Pseudocode is a high-level description of a product or a process, meant for your eyes only, generally written in the comments of the individual files you're set up in.
00:04:04.140
Let’s create an example that illustrates this. Suppose 'Creature Comforts' needs a new feature. This application, used by a local Humane Society, tracks all the animals from the day they come in to their adoption day and into their forever homes. Part of the application includes a daily task list for volunteers to see which animals need things like water, walks, playtime, or kennel cleaning. This list gets updated frequently throughout the day.
00:04:37.200
Now, the local Humane Society wants to add a new feature that calculates the daily amount of food each animal needs and adds it to the daily list. For example, if an animal needs water and also needs a walk, it should also show that it needs two scoops of dog food.
00:04:56.170
The story card we receive is as follows: 'As an adoption center employee, I want to have the daily amount of food calculated for an animal based on its type, which in our case consists of dogs and cats, and its weight. This amount will be added to their daily routine list.' After an emotional check, I’m feeling pretty good reading through this story card—it all seems to make sense to me. They just want to generate the amount of food required for each animal.
00:05:27.490
However, as I open up the file again, panic starts to set in. It’s time to go through test-driven development and set up that pseudocode. I’m going to create a pseudocode.rb file to organize my thoughts. I’m doing this all in Ruby, but you can use any programming language.
00:05:54.070
The first thing I’m going to do is perform what I refer to as a 'pseudocode dump.' This might involve copying and pasting the story card into the file comments. For me, I prefer to read through the story card and write it in my own words. I note that within the daily routine, we need to calculate food based on animal type and weight.
00:06:23.590
Ten pounds is set as the weight threshold for both dogs and cats. After reviewing the story card, I realized I didn’t have all the necessary information, so I asked around and found out that ten pounds is the weight threshold we need, which is great because it simplifies our task a bit.
00:06:49.700
In my pseudocode, I start describing a class called Daily Routine and methods to calculate food based on animal type and weight that we will need to create to utilize that method. This way, I’m breaking down the story and making the task seem less daunting.
00:07:09.080
Next, I write more structured pseudocode, focusing solely on dogs to simplify my goals. I find that setting smaller goals increases my mental clarity and decreases the negativity I feel when taking on the entire story card at once.
00:07:27.890
So, in this focused pseudocode, I will create a method for calculating food in the daily routine file. I'm starting only with dogs over ten pounds. Once those tests are passing, I'll add the functionality for dogs under ten pounds. This incremental approach for tackling the story allows me to learn as I go.
00:07:50.320
The next step involves writing more structured pseudocode. In this structured pseudocode, within the routine class, I denote a question mark for the animal type, as I am not sure yet how to fully define it. Right now, I am only focusing on dogs.
00:08:08.880
We have the 'calculate food' method that will require both the animal type and animal weight to determine how much food we're calculating. Additionally, I note to myself: 'Over ten-pound animals get two scoops, while under ten pounds get one scoop.' This organization will guide the implementation.
00:08:32.840
After examining this, a question arises: Do we need to specify whether it’s dogs or cats, or can we simply state 'two scoops of food'? The Humane Society requests that we provide specific animal types.
00:08:54.440
Thus, we’ll need to separate our approach for cats and dogs: dogs over ten pounds get two scoops of dog food, while under ten-pound cats get one scoop of cat food. I check in with my emotions and realize I’m feeling a little more relaxed now that I have some pseudocode outlining my plan.
00:09:15.820
Now, it’s time to introduce RSpec, a testing language written in Ruby for Ruby testing, designed specifically with test-driven development in mind. It provides blocks to set up code, creating a clean sentence structure which helps track where we are within the tests.
00:09:30.030
First, let’s discuss 'describe' blocks. These are used for a class or a method, such as 'RoutineCreator' or 'calculate food.' I envision my RSpec tests as an upside-down triangle: the 'describe' blocks form the widest part, which keeps the descriptions general.
00:09:51.290
Now I’ll set up the 'RoutineCreator' spec file and strategize how to describe the 'RoutineCreator.' I will then add a 'let,' which is a way to introduce a new instance of a class for the test to access its methods and variables.
00:10:10.220
After that, I’ll describe the 'calculate food' method because we know that we need to test this in alignment with the story card. This arrangement helps lay out our plan for what we want to test.
00:10:33.370
Next, we have 'context' blocks, which define the contexts in which our 'describe' blocks will operate. This might seem confusing at first, but I think of it as further narrowing down the particulars of our situations.
00:10:47.490
For example, under the description of the 'Routine Creator' class and the 'calculate food' method, we need to ensure that when the animal type is a dog, we assess its weight correctly, meaning there will be two contexts: one for dogs and one for the respective weight check.
00:11:04.030
It’s all beginning to take shape, resembling a sentence: the 'calculate food' method when the animal type is a dog and weighs over ten pounds should return the correct amount of food.
00:11:28.700
Next are 'it' blocks, which specify the test cases. They will lay out what we will expect to happen, which is the narrowest part of our triangle. For instance, we will check if it returns the proper amount of food.
00:11:48.700
So, I will run the test now with this skeletal setup of describe, context, and it blocks. Upon running it, the tests fail with an error message indicating that there’s an uninitialized constant 'RoutineCreator.' We realize that we need to ensure that we require the correct Ruby file.
00:12:06.740
After analyzing the confusion within the tests, I append the require line to the top of my spec file and focus on getting things set up. I run the test again, and sure enough, it fails with another error: we cannot load such a file 'RoutineCreator.' This tells me that I need to create that file.
00:12:28.380
Next, I will recreate the 'RoutineCreator' Ruby file, making it a blank one for now just to bypass the error message. Running the tests again reveals yet another error message that’s similar to the first one: 'uninitialized constant RoutineCreator.' I know I required that reference earlier!
00:12:46.510
I go in and define a class called 'RoutineCreator' to see if that resolves our issue. I’m feeling optimistic, but running the tests confirms another failure. Sighting multiple test failures can be disheartening, but with TDD, it serves as a pathway to see where improvements are necessary.
00:13:05.740
One of the error messages indicates that we’ve passed in the wrong number of arguments. Our test was given two, but we only expected zero. Taking a look at our tests, we find that while letting, we initialized the 'RoutineCreator' class but did not account for the required arguments.
00:13:25.570
I know what I need to do: create an initialize method within my actual Ruby code, passing in animal type and weight as arguments. After defining those variables and running the tests again, success! The tests pass now, which indicates we’ve made progress.
00:13:44.410
The skeleton for the code is set up, and I am feeling good. The harder part of setting up the tests is still ahead, but just achieving this foundational structure is monumental.
00:14:08.830
Now, let’s focus on the expect blocks within this test file. This block specifies what we expect the tests to yield or correspond with, in our case, the proper amount of food. Hence, we expect the 'RoutineCreator.calculateFood' method to return two scoops of dog food.
00:14:31.540
When I run the tests, they fail once more, but I anticipated that since we didn’t set up that method within our Ruby file. I go ahead and establish the 'calculateFood' method.
00:14:53.750
Running the tests again leads to a different failure. We expect 'calculate food' to yield two scoops of dog food, but we receive 'nil.' This indicates progress; we made it through our test file, moving to the final stages.
00:15:13.600
I know the next step is to add the smallest amount of code necessary for the test to pass, so I simply return two scoops of dog food from the method. I run the tests once more, and this time they pass.
00:15:32.180
This is the kind of progress that can be committed. I now have clear, committable code demonstrating our approach. The 'RoutineCreator' class is set with the 'calculate food' method returning the expected two scoops of dog food for animals that meet the specifications.
00:15:51.230
Encouraged by this success, I check back with my pseudocode. We’ve accomplished quite a bit so far! The next step is to add functionality for dogs under ten pounds.
00:16:10.720
So, reviewing my structured pseudocode, I note that we’re already set up for the calculation of food, based on weight and type. For dogs over ten pounds, we expect two scoops of food, and for those under, one scoop.
00:16:33.420
For this task, I will create a context that represents animals under ten pounds, specifying to anticipate one scoop of food. I am confident as I set up my test to return the expected result.
00:16:53.260
I predict my tests will initially fail since we haven’t included any logic to account for this condition in the Ruby file yet. Upon running the tests now, they do fail as expected, given we’re attempting to retrieve one scoop of dog food but received two.
00:17:14.190
Resolving this requires minimal adjustment: I’m going to add an if-else statement in the 'calculateFood' method. I can state that if the animal weight exceeds ten pounds, it’s returning two scoops of dog food. Otherwise, it should return one scoop.
00:17:36.970
As we watch our tests progress, we expect them to now pass thanks to our new logic, confirming that we’ve incorporated our checks efficiently into the code.
00:17:58.460
The tests pass, and now we’re left with the proper output for our dogs over ten pounds and under ten pounds, which reinforces that we can move forward with satisfying both conditions. I feel great!
00:18:19.540
Next, we are going to focus our efforts on cats. Following similar steps for cats that weigh over ten pounds, I’ll define the context within my tests: if the animal type is a cat and it weighs over ten pounds, we expect it to yield the corresponding amount of food.
00:18:44.310
I don't anticipate the tests to pass yet, as we’ll still need additional logic in our Ruby file. So, I execute the tests, which inevitably fail with the message indicating the food allocation was nil.
00:19:03.790
I’ll go on to add the missing logic: my statement: 'If the animal type is a cat, and its weight is greater than or equal to ten pounds, I’ll account for two scoops of cat food.' Running this test now yields successful results, which I can absolutely commit.
00:19:24.750
Now, it’s onto accounting for cats weighing less than ten pounds, with a similar setup where we expect one scoop of cat food. I anticipate that this test run will initially fail.
00:19:46.860
Indeed, I see that we expected one scoop of cat food, but once again received nil. I can resolve this by implementing if-else logic.
00:20:06.580
With all this in mind, I adjust the Ruby file, ensuring I return one scoop of cat food if the weight condition is met properly. Again, success floods in as the tests pass.
00:20:28.240
With that done, I feel quite accomplished! This iterative approach has worked well to progressively complete the story card.
00:20:51.700
My 'RoutineCreator' spec file is now thorough; it checks for the correct amount of food according to every relevant condition. The confidence boosts as I realize the tests validate my logic throughout.
00:21:13.790
One of the best parts of reaching this stage is that I can always revisit my code with a mind to refactor. Since I have solid tests in place, I can confidently introduce methodological improvements without fear.
00:21:38.790
It’s satisfying to know I can progress and add the results to the routine creator's routine list for volunteers, thus completing our initial feature request.
00:21:59.540
I’ve advanced beyond that panic stage and the negativity that once held me back. My confidence has improved—the feeling of overcoming the blank page panic is invigorating!
00:22:15.310
Thus, I’ve shared my journey of tackling and overcoming that panic, and I’m genuinely curious to hear about your experiences. Whether it’s test-driven development or another method, I’d love to hear your feedback.
00:22:37.510
Feel free to tweet me at @ElayneTalks or send me a direct message. I am eager to learn about how you build confidence and navigate through the initial challenges of starting new features or integrating with existing code.
00:22:59.670
I sometimes experience impostor syndrome when looking at other people's code, feeling overwhelmed by complexities that confuse me. Sharing insights on what you do to remedy such feelings could be so helpful.
00:23:23.600
Here’s a reference slide that encompasses some of the insights I shared in this talk. Most of it derives from my personal experience and a dive into the psychology behind writer's block and finding ways to overcome that panic feeling.
00:23:45.700
Thank you so much for listening! I hope you all have a wonderful day and continue staying safe and healthy!