Companies are built of many projects and repositories. Each tends to develop its own culture and tools for interacting with it.

The client is compiled with MSVC, and tests are run with ./runner.py --test foo/bar/baz_test.py.

The website builds with SCons and tests are run with bin/run-tests.sh.

The utility library has ./build and ./test.

Over time, some projects put scripts in the root, some in bin, maybe some in a directory named scripts. Some projects might use all three.

This makes it hard for people coming to the project to know 1) how to do common project tasks and 2) what the set of available commands even is.

When code is collectively owned and engineers contribute across the entire stack, it’s important that anyone can easily check out a repository and start developing in it, no matter the language or tooling.

At IMVU we solved this problem by introducing a directory to every project named s/.

  • How do you build the project? s/build
  • How do you run the tests? s/test
  • How do you deploy? s/deploy
  • How do you lint? s/lint
  • How do you launch the program? s/run
  • What if there’s a server component? s/server
  • How do you see what commands are available? ls s

At first there was some resistance, primarily to the name. “s is too short. Nobody will know what it means.” But after living with it, it was perfect:

  • It’s short.
  • It’s on home row for both qwerty and dvorak.
  • Unlike bin and scripts, s doesn’t already have a semantic meaning. bin, for example, is often used for programs generated by the build script. Conflating that with project tooling is confusing.

s is a home only for project commands – the interface that developers use to interact with the project. These commands should use the same nomenclature as your company (e.g. if people say build, call it s/build; if people say compile, call it s/compile).

The scripts shouldn’t have extensions, because, importantly, the programming language is an implementation detail. That is, s/build.sh or s/build.py are wrong. s/build lets you be consistent across projects and have the option to migrate from Python to Bash or whatever.

s/ is a simple trick, but it goes a long way towards helping people migrate between projects!

4 thoughts on “s/”

  1. I’m curious where you would recommend ci scripts would fit into this. Would you also put them in s/? I mean, they are purely project tools, but they are also not really meant to be used directly by the developer to interact with the project.

  2. Good question! I like ci/ for those. The thing about CI scripts is they often muck with the environment, so they should generally be kept separate from things in s/

  3. Sounds like a standard set of make targets to me.

    How do you build the project? make build
    How do you run the tests? make test
    How do you deploy? make deploy
    How do you lint? make lint
    How do you launch the program? make run
    What if there’s a server component? make server
    How do you see what commands are available? make targets

    So often it seems like a new problem but really most of them were already solved in the past.

  4. If you have make generally available on the system, yeah. On Windows that wasn’t necessarily guaranteed, so we put a combination of shell scripts and batch files in s/. But yeah, you’re right that you can use whatever convention your project uses. I’ve worked on projects that used npm as a launcher even without relying on Node libraries. But I like s/ because it’s trivially enumerable, has tab completion, and works on Windows with batch files too.

Leave a Reply

Your email address will not be published. Required fields are marked *