At Inferless I authored the CLI that developers use to deploy models and test them locally against Inferless-managed environments. A CLI is a product — its UX matters as much as any web app.
Principles I kept coming back to
- Commands should read like sentences.
inferless deployandinferless runover flag soup. - Errors should tell you what to do next, not just what went wrong.
- The happy path should be one command.
A taste of the interface
# log in once
inferless login
# scaffold, then deploy
inferless init my-model
inferless deploy --gpu A10
# test locally against a managed runtime
inferless run --input ./sample.jsonErrors that respect the reader
Compare the difference a good message makes:
class DeployError(Exception):
def __init__(self, reason: str, hint: str | None = None):
self.reason = reason
self.hint = hint
super().__init__(reason)
def require_gpu(gpu: str | None) -> str:
if not gpu:
raise DeployError(
reason="No GPU specified for this model.",
hint="Pass one with --gpu, e.g. `inferless deploy --gpu A10`.",
)
return gpuRendered, that becomes:
✗ No GPU specified for this model.
→ Pass one with --gpu, e.g. `inferless deploy --gpu A10`.The payoff
Good DX compounds. When the CLI is predictable and its docs match reality, developers self-serve, file fewer tickets, and trust the platform more. That trust is the whole game for a developer tool.