Coding Guidelines
Our philosopher-in-chief has written, "[Programming] is about making sure that you know how and why your code works, and that you and your readers will so in the future." Every revision to the Racket code base should be consistent with this epigram by providing:
Matthias is also known to say, "I have bug reports, therefore I exist." All software has mistakes. If they are not known, then the software is not being used. Robby writes, "It is the way we choose to fight our bugs that determines our character, not their presence or absence." Read these guidelines in that light.

As long as your code fulfills its promises and meets each of these criteria, you can push incrementally.

For example, I may be working on adding an exhaustive queue library to Racket. I imagine supporting 30 different functions across 5 different queue implementations, for a total of 150 functions. If I have just 30 of these documented, tested, and stressed, then it's fine for me to push.

It is better to make a little bit of progress on each of functionality, tests, and documentation than lots of functionality with no tests or documentation.

Documentation

First, refer to the style guide in the Scribble manual.

Next, consider splitting your manual into a "Guide" section, that suggests use cases and explains the purpose of the code, and a traditional "Reference" section that presents the minutae. You may also consider adding examples to each function in your "Reference" section.

Finally, ensure you have all the correct for-label requires and make use of other useful cross-references.

Tests

First, write an automated test that exposes the mistake in the existing implementation. Put this in the software's test suite so it will never be accidentally introduced again.

Second, modify the code to fix the mistake. Do this second to be sure you didn't introduce a mistake in your tests; it is all too easy to think you've fixed a mistake when in reality your new test just doesn't properly reveal the old mistake.

Last, re-run the test suite to ensure that the mistake is fixed and no existing tests fail.

"Primum non nocere"

If there is no existing test suite, you have no idea whether changes are introducing any new mistakes or breaking intended functionality.

You should make a reasonable effort to put in a test case or two for the specific functionality that you're adding or modifying. If there is no test suite and you aren't sure how to build one, then ask, see what responses you get, and go from there.

In the special case that you found a mistake and are fixing it, there should be a way to observe that mistake and you should be able to turn that into a test case. If you cannot, you have a few options:

  • Add an end-to-end test that may have to be verified by a human. For example, it might be hard to test Slideshow, so you could create a slide set and describe what it should look like so future maintainers to verify when they make changes.
  • Add functionality to the library to expose properties that reveal the bug. For example, you may be able to add a few accessors to Slideshow slides to enable an automated test suite.

As we slowly increase the number of tests across the system, this problem will go away for future generations.

Stress Tests

A test case ensures that functions fulfill their purpose and have the correct error behavior. A stress test exposes your function's performance and complexity.

For example, a test case for a queue library ensures that it deals correctly with enqueue and dequeue using small queues (say 3 or 4) and tests the corner cases. In contrast, a stress test for the same library would run the queue operations on a variety of queue sizes, including very large queues.

Stress tests don't normally have an expected output, so they never "pass". The practice of writing stress tests exposes implementation flaws or provides comparative data to be used when choosing between two APIs. Just writing them and keeping them around reminds us that things can go bad and we can detect when performance degrades through some other door.

Refer to the tests/stress/stress library.

It contains a macro for running experiments that should behave the same but may have different performance. After running the experiments in isolation (through custodians and periodic garbage collection), it reports sorted timing information for comparative purposes.

It also contains a function for running a function on larger and larger numbers and computes their relative timings.