---
title: Using a Function Design Recipe to Debug
teaser: 'Ever notice how some of your bugs are annoyingly simple once you find them?
  Use this six-step checklist to skip straight through to harder problems.

  '
tags: debugging,debugging series 2021
author: Mike Burns
published_on: 2021-10-01
---

The book [How to Design Programs][]<sup><a href="#fn1" id="fnr1" title="see footnote" class="footnote">1</a></sup> introduces the concept of a function or
program's [Design Recipe][]. I'll quote the list here:

[How to Design Programs]: https://htdp.org/
[Design Recipe]: https://htdp.org/2021-5-4/Book/part_preface.html

> 1. From Problem Analysis to Data Definitions
> 2. Signature, Purpose Statement, Header
> 3. Functional Examples
> 4. Function Template
> 5. Function Definition
> 6. Testing

When I taught programming to teens with what is now called [Bootstrap][], we
used the Design Recipe to help the students debug their own programs. We'd ask
them "which step of the Design Recipe are you on?", and get one of two answers:
"I don't know", in which case we sent them back to step 1, or they'd tell us a
step number and we'd talk through that or the prior step in more detail.

[Bootstrap]: https://bootstrapworld.org/community/index.shtml

This is an effective method that I still use today, even on myself, both when
writing new code and debugging existing issues. Let's examine this in more
detail.

## 1. From Problem Analysis to Data Definitions

The first step is to check your data structures and make sure they match the
problem you're trying to solve.

As a simple example, the problem statement might be: as I crawl the Web I want
to track which pages I've seen so I don't crawl them again. The right data
structure for this is a set, but you could possible try an array (with
progressively slower crawls and much more memory usage), a tree (storing
everything as you go -- again, slower and more memory usage), or a graph
(accurately storing everything as you go). Selecting the wrong data structure
gives you a shaky foundation for debugging.

## 2. Signature, Purpose Statement, Header

We can start by checking the comment describing the method. Try writing one if
you don't have one already -- express what you think the method does in your
own words. This helps solidify the idea more conceptually. For style points,
do this based on the problem statement and not the buggy code you had written.

Next up, check your types. This goes for you using a static type checker, too!
Are you passing the right values to it? Do you handle the null value that it
returns? Are you passing in all the data that you need in accordance with the
purpose statement?

## 3. Functional Examples

This is a great time to check on your automated tests. Make sure they capture
everything described in your purpose statement, at minimum.

Try to write a test for the actual bug. This has a number of benefits in
itself, plus it helps with debugging. An automated test can help prevent the
bug from recurring. Writing the test can inspire other tests -- if you notice
that you're testing on bogus input like null you might want to test on other
bogus input like a blank string or the wrong number.

Writing a test for the actual bug also gives you confidence that your fix will
work, and in part this comes from forcing yourself to understand enough of the
problem to write the test. It clarifies and reinforces your understanding of
the bug.

## 4. Function Template

The HtDP Function Template is a neat idea that points out that, given a data
structure, you can write most of your function without even knowing what the
function is supposed to do. For example, if you have a binary tree you know you
need to do something different based on whether it's a leaf or a node, and you
know a leaf needs a value and a node has a left and right. You don't need to
know what the `???` operation is just yet:

```ruby
def arbitrary_tree_function(a_tree)
  if a_tree.leaf?
    ??? a_tree.value
  elsif a_tree.node?
    arbitrary_tree_function(a_tree.left) ??? arbitrary_tree_function(a_tree.right)
  end
end
```

This pertains to debugging, too: you can look for issues in a function without
even knowing what the function is supposed to do. Is there a branch you're not
considering? Do you have an enum that should have a case statement (and do you
need a default clause)? Are you ignoring a value?

## 5. Function Definition

At long last, let's look at the actual implementation. This uses both the
purpose statement and the examples (the tests). Does the implementation match
the purpose statement? Does it actually do what the tests say it should?

Debugging at this point might be the most creative and challenging part, so I
recommend looking elsewhere in [our debugging posts][] for advice, but the
important thing is that you know _it's something specific to your problem_. The
types are correct, the function's structure is right, the tests are in place,
you understand the problem well enough to rephrase it in your own words, the
data structure is right. You are now fighting a much simpler battle than you
were before you were sure of this.

[our debugging posts]: https://thoughtbot.com/blog/tags/debugging

## 6. Testing

If you still can't get it, try to leave a (perhaps commented out) failing test
for the next developer. Just writing this might give you one last creative idea
and bug fix; even if not, it might give someone else the spark they need to
uncover the solution.

If you have a failing test that you're leaving for the next developer to figure
out, your testing framework might have a "this is supposed to fail" mechanism,
such as RSpec's [`pending`][]. This way future developers will discover when
they accidentally make it pass.

[`pending`]: https://relishapp.com/rspec/rspec-core/v/2-0/docs/pending/pending-examples#pending-any-arbitary-reason,-with-a-block-that-fails

## Conclusion

The simple Design Recipe checklist can become as comfortable as the
keys-wallet-phone pocket check you do before you leave the house, and gives you
a launching point for evaluating your code and asking for help on the right
points. Give it a try the next time you're stuck!

## Debugging Series

This post is part of our ongoing [Debugging Series 2021] and couldn't have
been accomplished without the wonderful insights from interviews with the
following people:

- [Adam Sharp](https://twitter.com/sharplet)
- [Eebs Kobeissi](https://twitter.com/EebsKobeissi)
- [Eric Bailey](https://twitter.com/ericwbailey)
- [John Schoeman](https://github.com/johnschoeman)
- [Mike Burns](https://thoughtbot.com/blog/authors/mike-burns)
- [Rick Gorman](https://linkedin.com/in/rickgormannyc)
- [Sally Hall](https://github.com/sallyhall)
- [Sam Kapila](https://www.linkedin.com/in/samkap)
- [Sarah Dawson](https://github.com/knittingarch)
- [Sean Doyle](https://github.com/seanpdoyle)

[Debugging Series 2021]: https://thoughtbot.com/blog/debugging-series-2021-welcome-to-the-jungle

<ol class="footnotes">
<li id="fn1">
One of the authors of the How to Design Programs book has since become a <a
href="https://beautifulracket.com/appendix/why-i-no-longer-contribute-to-racket.html">controversial
figure</a> with views about DEI that go against <a
href="https://thoughtbot.com/playbook/our-company/diversity-equity-and-inclusion">thoughtbot's
views</a>. The ideas in this work come from a time when he was more silent
about such issues. The three other authors have been fully supportive of DEI
efforts in computer science and the industry.
<a href="#fnr1" title="return to article" class="reversefootnote">&#8617;&#xFE0E;</a>
</li>
</ol>
