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.
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.
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:
As we slowly increase the number of tests across the system, this problem will go away for future generations.
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.