Featured image of post A flag is not a setting

A flag is not a setting

I was reviewing a change to rust-tool-base’s scaffolder when a word stopped me dead. rtb generate config-field. I couldn’t have told you why in that first second… I looked at it and just knew it was wrong.

The verb there is generate, and the verb is fine. It was the noun that grated, config-field, the name of the thing being made. Renaming it is a small change. It’s also a breaking one, and a gut feeling is no reason to break someone’s command, so before I touched it I went and worked out what the instinct was reacting to.

Accurate, and still wrong

Here’s the awkward bit: config-field is correct. The thing it makes really is a field on a config struct. If that name lived deep in a package, somewhere only another developer reading the source would ever trip over it, it’d be fine. The code’s audience is me, and “config field” is exactly what the code sees.

But it doesn’t live deep in a package. It sits right out on the command line, on the one surface a user actually types, and a name out there has a different job. It has to telegraph what it does to someone who has never read a line of the source, in words a layperson would reach for. By that test config-field fails, and not because it’s wrong. It fails because it’s right about the wrong thing. It describes the plumbing when all the user wants is to turn on the tap.

That’s the rule I keep coming back to anywhere a person actually touches the tool: accurate is the floor, not the bar.

What the noun names

The right name falls out of what the thing actually is, so I went and pinned that down. rtb’s scaffolder makes three different things, and the noun is how you pick which:

rtb generate command <name>   # a new subcommand
rtb generate flag <name>      # a command-line argument on a command
rtb generate setting <name>   # a field on the tool's typed config

A flag and a setting sound like cousins, but they answer two different questions: where does the value come from, and how long does it live. A flag is something the user types for a single run (a clap argument, in Rust terms), like deploy --region eu or --dry-run. Transient, scoped to the one command. A setting is a typed field on the tool’s AppConfig, read from its layered config: a file, the environment, or a one-off override on the CLI. Persistent, and tool-wide. (The full contrast is its own doc now.)

Put the two side by side and the old name gives itself away. flag says what the thing is to a user. config-field said what it is to the code. One tells the truth at the surface; the other leaks an implementation detail you were never meant to care about.

Why they’re two things at all

This is the bit that makes the rename honest rather than fussy, and it’s where rust-tool-base and go-tool-base part ways.

In go-tool-base, a flag and a setting are pretty much the same object. cobra and viper (Go’s CLI and config libraries) fuse them: you bind a flag, viper reads its value from a config file or the environment, and you’re done. One persistent flag laid over a config bag. That’s no compromise, it’s an excellent convenience abstraction, gtb leans on it to the hilt, and for what it’s worth it’s the model I personally find the simpler of the two. One mechanism, one thing to keep in your head.

Rust won’t hand you that fusion, and it’s right not to. rtb’s config is a typed AppConfig (built on figment, a Rust config library), not a dynamic get_string("key") bag, so a command-line argument and a config field genuinely are different types with different lifetimes. Splitting them isn’t rtb being puritanical about it. It’s the shape Rust’s type system gives you, and the framework leans in and makes the most of it. The rtb version is, no argument, the more type-safe of the two.

So neither is better. They suit different paradigms, and both do the job beautifully. But the knock-on for naming is concrete. Once a flag and a setting really are two different things, calling one of them config-field doesn’t just expose the plumbing, it tells a small lie: it implies a setting is the same kind of object as the struct field it happens to sit in. setting tells the truth. This is the thing you configure once and the tool remembers, the sibling of flag.

(rtb has form here, mind. “flag” already pulls double duty: a runtime feature flag and a compile-time Cargo feature are two more genuinely different things the framework keeps deliberately apart. Stretch one word across that many concepts and naming each one precisely stops being pedantry and becomes the only way anyone keeps them straight.)

The change

So config-field became setting, and picked up its mirror image remove setting to round out the trio of command / flag / setting. It’s a breaking change, rtb generate config-field is gone for good, and it earned its keep. The cost is a line in a changelog. The return is a command surface that says what it means.

Name the tap

The gut reaction was right, but the gut reaction was never the point. The point is what it was reacting to: a name, out on a surface a human uses, describing the machinery instead of the job. config-field was accurate. It still made the user stop and think about a struct field when all they wanted was to set something up and get on with it.

Nobody turning on a tap wants to think about the pipework behind the wall. Name the tap.

Built with Hugo
Theme Stack designed by Jimmy