I wrote a little mini language[1] for a project last year (I only inflicted it on myself, not any poor user). It served its purpose well: allowing me to quickly implement people's requests in a way that was sandboxed/isolated from other people's accounts, was impossible to take the system down no matter how bad the bugs, and I could implement these requests without redeploying the underlying system. In that respect, it was a huge success (and that's what it was for). But in every other way, I think it was a failure: I came to hate writing code in it, despite having designed it. It had missing language features that I never got around to implementing, strange bugs that I didn't have time to fix (so just avoided doing those things), nobody else but me could use it (the project was just me developing, but the users couldn't use it either despite having access to it. It replaced a simple but very limited command language, but in doing so added too much real programming). So while it succeeded in what it set out to do, it wasn't a sustainable or future proof solution. I did learn what users actually wanted though and got to test/prototype it before adding it to the underlying system. I'm also working on a declarative replacement now (basically a TOML file where values can be expressions -- a template system), that supports everything the users actually want, but is vastly simpler and has much less custom logic.
It was a great learning experience though and I'm glad I did it, both in terms of learning how to write grammars (I used Clojure's Instaparse) and interpreters, and in learning about the users needs and wants.
But I shudder to think about if anybody else would have had to use it...
[1] Some interesting (IMHO) technical details: it was a synchronous language (of the transformational system variety), meaning that while it was executing, time was essentially frozen: it would gather all of its inputs, then evaluate the code as a pure transformation of its inputs (made it very easy to interpret!) and then write its output. The inputs are essentially immutable while the evaluation is happening. This was run in a database transaction for consistency. Each individual script was basically stop-the-world non-concurrent, but the scripts were independent and many could execute at once. I'm a very big fan of event driven synchronous languages.