DEV Community

Cover image for How to sanely configure your applications
Jon Lauridsen
Jon Lauridsen

Posted on

How to sanely configure your applications

There is an awkward friction in frameworks that offer to start your application, like rails serve and next start, because they swallow up your application so they're in charge of it, rather than you owning it. Today I'll focus on one specific area where this friction can get very visible: Configuring your application.

I've seen my share of applications that have not put enough thought into their configuration, and the result was a mishmash of using the framework's conventions and reading environment variables in random places. In several cases the values weren't validated at all, causing obscure runtime errors if the environment was not configured exactly right. In this context new-joiners have no way to understand what configuration options exists or what they really do other than searching for them in the code. It's brittle madness.

This article is a highly opinionated example of how I like to take ownership of the application startup process, but I'd very much like to hear how you prefer doing it because I'm sure there are great alternative designs out there that are well worth considering.

(as long as it doesn't involve reading environment variables all over the place!)

Just to be clear, when I say "application" I mean anything from long-running services that handles customer requests, to tools and utilities used in pipelines and batch-processing contexts.

And what is it I'm selling? What I suggest is this: Our applications should work like any other application, and the simplest, most tried-and-true approach for almost all applications is: The Command Line Interface (CLI). The decades old, evergreen practice of passing arguments to a program using the terminal. Our applications are no different than ls or dir or any of the commands and programs we use every day, so why should the applications we write behave any differently? CLIs are the easiest and most intuitive interfaces because we all know how to use them and they offer great discovery by letting new developers explore each possible argument.

What exactly do I mean with CLI? At the very least it means being able to pass --help to understand how to use the application. If we're making the Cool Awesome Hats web-store service then what is simpler than running coolawesomehats --help and have it explain how to get started?

$ cah —help
Usage: cah [-dph]

cah starts the Cool Awesome Hats web store, letting customers buy cool & awesome hats.

-d —db-connection-string      Connection string for a Postgres database
-p —port                      Port to serve on, default: 8080
-h —help                      Show this screen
Enter fullscreen mode Exit fullscreen mode

With this approach developers can intuitively learn how to start the app, and there are no mysterious, unknowable environment variables to stumble across. Configuration is specified in one and only one place: The CLI.

This implies a CLI parsing module whose only responsibility is to read and validate argument inputs and instantiate some sort of application configuration object, which can then be passed into the application itself. In Typescript it could work like this:

interface Configuration {
    dbConnectionString: string;
    port: number;
}

function parseCli(argv: string[]): Promise<Configuration> {
  // Parse & validate argv
}

function startService(conf: Configuration): Promise<void> {
  // Start the server and connect to DB
}

parseCli(process.argv)
  .then(startService)
  .catch((err) => {
    console.error(err)
    process.exit(1)
})

// Or if you're allowed top-level awaits:
// await startService(await parseCli(process.argv))
Enter fullscreen mode Exit fullscreen mode

This way there is a simple, explicit boundary between configuration and the service, and the CLI parsing module is easily unit-tested which gets increasingly important when dealing with more and more options.

Of course this was just a simple example and real-world applications may have many configurable parts. Thankfully CLIs scale well, and are very flexible in terms of the inputs they can be made to accept. I recommend the CLI approach as a great way to centralize and clarify configuration, and to make it easy to explore the options that are available.

Photo by Jon Tyson on Unsplash

Discussion (0)