Here’s the code for some Game of Fifteen implementation. (the I : -1- -15){the Tile = "tile_" & I; ( Tile- - ) ( ((Tile- ) - ("tile_16"- ))+ ((Tile- ) - ("tile_16"- )) == 105 ) -Tile- -"tile_16"- -200- , -"tile_16"- -Tile;} -"cursor"- -( -x)-( -y); for from to if is clicked if abs x x abs y y slide on in ms teleport on teleport to mouse mouse What can you say about the language this piece is written in? You may probably conclude that it is some kind of DSL. It has some very application specific functions like and . It is verbose, but it is meant to be read by someone who doesn’t know all the conventions like do you set time in hours or milliseconds. It has some kind of dynamic typing and its functions may receive arguments from both ends. slide teleport But what is this language anyway? It’s C++. Here you can . It’s a completely valid C++ piece of code written in a language inside a language. And this nested language is completely different from the host one. It has different typing and different syntax. But it’s still C++. see for yourself Never mind this piece though. It’s not important, it’s only an example of how deep one can go. And ‘can’ doesn’t mean ‘should’ anyway. Rather the opposite. What’s important is that the edge between making a new language and expanding an old one is rather thin. You don’t have to sit in a committee or be a compiler designer to make a language. In fact, making the language is the part of every programmer’s day-to-day job. Every time you introduce a new word: write a function, or a data type, or a type class— you make a bit of a new language. And while the idea of each team member contributing to the whole system by writing in his own language, like Forth, Python and Java, is horrifying, the reality of each team member making his own language without giving it a name is somehow less so. But it’s basically the same thing. Bluntly introducing new words to the language doubtlessly increases the complexity of the code written in it. Of course you can actually reduce complexity by providing good modularity, and each module would require a name hence adding a new word to the language. But that’s an investment. You buy some simplicity with some complexity. Still every investment needs a good ROI justification. Is this abstraction layer truly unavoidable? Are these wrappers helpful? Who will benefit from this data types redefinition? Do you really want this method? IsZero() These are not at all rhetorical questions. Sometimes you do, sometimes you don’t. Sometimes you just don’t know, and you have to learn more about your users and how do they plan to benefit from your work. But this will pay off eventually with simpler code and happier users. This little exercise — thinking of adding entities to the system like an investment — is the key to stop making a new language nobody really wants to learn and start living.