I recently invoked the help text on Rand, a tiny command-line utility I wrote to generate random values in formats I use frequently, and reminded myself of a cool feature I added more than two years ago: dynamic help text output. Rand has a bunch of switches and configuration options (with more on the way), so even the developer needs to consult the documentation to use it from time to time.

Here’s the help text returned by rand -h, which includes a few nicely formatted example invocations and their results.

In these examples, I’ve shown the user what to expect for various inputs and outputs, which is pretty useful for a utility like this. But seeing as how this is a tool that generates random values, why don’t we generate some random values right here in the examples? If I request the help text again, the otherwise static example outputs of rand all change.

In the source, the help text is implemented as a long format string, with some spots to insert the string values that will generated on demand.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const info = `A random string a number generation utility.

[...]

Examples:
  $ rand
  %s

  $ rand -luns
  %s

  $ rand -nuc20
  %s

  $ rand -c3
  %s
`

Then, when the help text is requested, we’ll generate the values using the same high-level API exposed to any code that imports this as a Go package, ensuring that the examples use the same code paths that actually generate the values during normal use.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func makeInfoText(info string) string {
	noArgs := rand.Make(rand.Options{
        Length: 8, 
        Numbers: true, 
        Lowers: true
    })
	luns := rand.Make(rand.Options{
        Length: 8, 
        Numbers: true, 
        Lowers: true, 
        Specials: true, 
        Uppers: true
    })
	nuc20 := rand.Make(rand.Options{
        Length: 20, 
        Numbers: true, 
        Uppers: true
    })
	c3 := rand.Make(rand.Options{
        Length: 3, 
        Numbers: true, 
        Lowers: true
    })

	return fmt.Sprintf(info, noArgs, luns, nuc20, c3)
}

I’ll admit that this is at most an easter egg-level feature. I’ll even go so far as to say that this might actually make the examples slightly worse. In the two screenshots above, the outputs of rand -c3 just so happened to first return only numbers, then returned only letters. Allowing the example values to be generated on the fly opens the door to this kind of coincidentally confusing or misleading output. What if, for example, rand -c3 returned ccc?

Being able to choose representative output examples in documentation allows a developer to be very specific about the intention of the tool and its options. Even if rand -c3 could potentially return ccc, using ab0 as an example response signals to the user that the output can include lowercase letters, numbers, and is three characters long. If this was ever used by anyone besides me, I’d spend more time considering the downfalls. For now, it’s a fun feature of a tool I hope to breathe some new life into.

Other applications where this could be useful (or even feasible) is an interesting topic to consider. Probably not a great idea for anything that makes network requests, or takes a long time to compute, or anything that could modify the host. A dynamic example of rm is a bad idea. But a quick random value generator? Sure, why not.