Written by a human, not by AI Bash The ubiquitous Unix shell and scripting language. DevTools Developer tools, CLIs, and productivity utilities.

Your developer environment needs a CLI

I’ve seen a lot of scripts throughout my career. Scripts are everywhere. It’s very common to see scripts for:

It’s also very tempting to try to reuse your scripts both locally and in CI. So these scripts tend to accumulate if CI conditions and end up pretty complex to understand.

Now we have LLMs so the natural instinct is to just throw them at the problem next time you need to write or modify these scripts. I’ve seen many attemps from developers to do that and the results are.. not good.

It’s not that LLMs cannot write Bash. They definitely can write Bash and they do it much better than humans. But this is where the problem is. LLMs will happily solve your problem in Bash no matter how hard it is. You end up with thousands of lines of Bash scripts that no human can understand. Argument parsing, validations, logic, error handling, everything is going to be implemented in Bash.

During code review this code just tends to be accepted as-is just because it’s so convoluted no one wants to read it.

Bash is not a language to write and maintain complex logic. It’s fine to have 10 line bash script once in a while but once you past this threshold then it’s much better to just use a general purpose language to solve the problem.

I came to realization that the only feasible solution to this problem is to ban all Bash usage and instead force developers to write their logic inside a CLI. It does not really matter what language you choose to write a CLI in but I would strongly recommennd doing it in Go or Rust. This allows you to compile the CLI into a small native executable which makes it easy to pre-compile and distribute to your developers and CI machines. Not to mention it’s going to be fast.

Using a proper CLI has immediate benefits:

  1. Control flow writtern in general purpose languages is more verbose but more explicit and maintainable.
  2. You can modularize and reuse code.
  3. Error handling and reporting is usually much better.
  4. You can tap into the standard library and ecosystem of your language of choice.
  5. You get access to high-quality argument parsing and configuration libraries.
  6. You can inject code to report telemetry and errors.
  7. … the list goes on

You don’t have to write any code yourself nowadays. LLMs will happily solve your problem in Bash, Go, Rust, TypeScript or any other mainstream language you thow at them. But you still have to guide the architecture. LLMs is not going to reflect on the choice of the language and it happily produce thousands of lines of Bash. Make a better choice now!