Skip to main content

Handing users a foot-gun, or a foot-bazooka? Eh, a well-designed potato-peeler it is.

Content warning: this is directed purely at programmers.

At work we're deciding what configuration file format to use for a new piece of software, where the configuration will have to be produced or modified by non-expert users. Of course, as the developers of the project, it's always easy to forget about these, your most important users: those who won't have a deep engagement with your project and how amazing it can be, but to whom it is only a minor help or hindrance on their path to 'getting shit done'.

Broadly, I think there are three choices you can make:
  • the bad choice: design a system around the sum of the complexity involved
  • the 'nice' choice: design a system that focuses on providing a straightforward view to the median user but is extensible enough that any kind of complexity can be represented
  • the lazy choice: design a system that only really accounts for non-expert users, forcing others to either water-down their dreams or build elaborate and disturbing external structures
I'm sure at this point people more familiar with UX concerns than myself are thinking 'stop stating the obvious'. And as always, it's a spectrum. Within a single project, you're likely to make many different judgements about where to draw the line between simplicity and complexity based on developer cost, perceived importance of various parts of the userbase, etc.

What I hadn't considered deeply enough before is how valuable forcing simplicity on users can be at all levels, because - with code, configuration files, tools, web interfaces, ... - the non-expert user will have to interact with the output of the experts. And if I'm an expert with a simple tool, I can do complex things, but if I'm a beginner I'm far more likely to understand the expert's usage of a simple tool than the expert's usage of a complex tool [insert snide remark here about configurability of JIRA]. So extensibility/configurability has a cost not just in development time, but in actual end-user experience, even if you have the perfect complexity-hiding abstractions.

Programming Languages

I'm most tolerant of complexity in programming languages, because there is some sense that it's a worthwhile expenditure of effort for anyone working on a codebase to become proficient in its idioms and structures. And, damnit, my brain likes to produce 'beautiful' code, which is hard if the language gives you only a single hammer, no matter how beautifully constructed (just don't talk about those languages that give you 100 poorly constructed hammers).

Nevertheless, if my code has stood the test of time with little bitrot for a year or ten, the chances are whoever is touching it will not be an expert. Thus the rational part of my brain says prefer Go over Rust, C over C++, and probably even Java 1.3 over Java 8. And even that modern Python is no longer a beacon of sanity, with its steady accretion of complexity and TIMTOWDI. Because really, what matters is the poor soul who has to dive into some weird code to fix a bug, not how much fun I had writing it. And if I'm given 100 beautiful hammers, there's no way I'll be convinced not to use them[1].

[blimey, why didn't I think of a better analogy. Perhaps musical instruments? Probably not as instantly comprehensible to people who aren't me...]

Command line text processing

I'm pretty dogmatic here: if you're one of those people who uses Perl one liners, jq or even awk to do anything persistent, I think you shouldn't. And even if you're doing ad-hoc processing, your time would be better spent learning cut/join/tr/sed/etc. over awk, and jmespath over jq[2]: because one day you will want to share some little bit of script, and your one-liner is more likely to be approachable for the non-expert if you wrote it with limited tools. And you shouldn't expect everyone you meet to become experts to be 'real programmers': becoming a CLI wiz is a hobby, not a job.

Although really, if you're doing anything you're going to check in to source control, just write a proper program. That means "don't use sh/bash". Please.

Configuration files

Finally, where I started out. I've been haphazardly arguing that we should use parameterised Jsonnet as the base for our configuration language rather than building a very simple template-ish system into our JSON/YAML[3], because I've internalised all those lessons from our forebears about being doomed to reinvent Lisp if you start building simple custom DSLs, or at least doomed to build some slightly less eldritch horror that will still cause future us to bemoan our stupidity.

This is a long ramble, then, to say that 'eh, you guys are probably right'. If we hand users the gun of Jsonnet, someone will probably go nuts, and the blast radius will hit the non-expert users somehow (either through cargo-culting or need to modify). Whereas if we straight-jacket our users, someone might still go nuts by building another abstraction layer, but it shouldn't spread too far or too fast. We'll at least have time to develop a vaccine or tweak our format to support the use case.

Footnoodles

  1. Unless there's some awesome linting tool? Or maybe only if I wrote the linting tool (not because it would be awesome, more likely the converse, but then I'd be sufficiently invested in it).
  2. Random rant: why is jq the most popular choice for JSON processing? I find JMESPath's handling of pipes/projections much easier to understand, and appreciate its integration of the most common operation (jq's select) into the filtering mechanism and avoidance of jq's confusing mess of filters/functions with the same names. And though it doesn't have fun things like exception handling and function definition built-in (complexity...), it is well-defined and has libraries in a bunch of languages (unlike jq's 'this is a C program' view of things), so you can cleanly step back to a well-understood 'non one-liner' programming language at a sane point before you're stuck in some horrible shell script rabbit-hole.
  3. Just a shout out here to JSON-like config, a spectrum of under/overengineering, which draws out a few things to think about. And shit, I wish HOCON (or even HJSON) was actually more popular and thus a more reasonable choice, but it feels like YAML/TOML are the current good-enough solutions for configuration files now (and IMO raw JSON is useless due to lack of comments).

Comments