I think there’s lots of things about elisp the language which make it easy to hack on too. In no particular order:

1. Dynamism+late binding+late failure means that, although it is hard to write fully “correct” code (and correct is ill-defined/impossible), it is very easy to write code that does work in the common cases without having to care about the uncommon cases.

2. Dynamism makes it possible to extend existing code with more functionality. You can get at any function and easily add your own code to run before/after/around it, or just redefine it to do what you want.

3. Macros make it much easier to do a lot with a little and mean there are far fewer language limitations than in other PLs because one can usually macro around them. E.g. emacs didn’t support lexical scope natively until recently but for a long time supported it with cl macros.

4. Dynamic scope makes global scope local, which means that having lots of globally scoped variables (for various eg options) isn’t so bad so there are lots of these and these then allow one to tweak the behaviour of functions by letting these variables. (e.g. if you have a function that determines some arguments for a program foo and then runs it, you can run that program remotely by doing (let ((foo-executable "ssh") (foo-other-args (append (list "some-host" foo-executable) foo-other-args))) ...)

5. Many internal data structures are simple arrangements of lists, symbols, and strings that are easy to inspect and easy to modify. They are so easy to modify that many come with a spec of their structure that is automatically turned into a gui to modify it.

6. Buffer-local variables are a super convenient way of attaching state to a buffer and a buffer is a super logical place to attach a lot of state to. There are some caveats, e.g. often one wants buffer groups of some kind but these are much less common in the simple case.

7. Symbols are much better than strings. They are faster, feel more natural, and are much easier to attach things to. It seems weird to imagine JavaScript having some global object mapping nearly every string to an object listing various different properties of that string but symbol plists tend to feel pretty natural and are a handy way to extend things.

8. It’s reasonably easy to not have independent things stepping on each other’s toes. Typically if you can write down a unique symbol for your thing then it won’t really conflict with other things. You can use it for global/buffer-local scope. You can use it for a property in symbol plists without stepping on other properties.

9. There are hooks for extension built in all over the place.

10. Your code gets to do things instantly. With many languages you can’t do the useful thing you wanted to do until you can get the data in/out of it which is often a lot of work. It’s even more work if you want something interactive, and even more work if you want something better than curses. Even for an “easy to get going” language like JavaScript you can’t really build on what’s come before. You typically have to start by building up most of the environment you want to work in by yourself first. In emacs it’s already there and you can use everything that’s there for free. There is no building up your environment because your environment is the one and only environment.

11. Essentially anything you can do with keys is just a regular elisp function called interactively. It is easy to make a function a command by telling it what things you care about which means that most commands correspond to functions that take extra arguments which are useful when calling non-interactively.

12. Nearly everything is native. The things that aren’t in elisp are typically only very fundamental or boring things. And all this code is on your machine, emacs knows where it is and you can read it. If you want to understand something you can jump to definition and have a look. You don’t need to wait for the package maintainer to fix things, just write the fix yourself and stick it in your .emacs. In other editors/systems the bug might be much deeper. You don’t want to have to build everything from source to be able to fix bugs yourself. In emacs you are effectively building everything interesting from source (except without really doing any work most of the time thanks to the abomination of unexec) so it easy to hack on.

13. Emacs has lots more experimental things than other editors/languages because the users write them (to scratch itches) and accept them. One can wonder why an emacs user might be more likely to accept rough edges than a user of (say) vscode. One could stereotype that the typical emacs user is more used to code that is rough or buggy, or that the typical vs code user is more used to code that doesn’t do anything useful. Or maybe it is just that emacs being rough around the edges is the price one must pay for the smug superiority of using what is (objectively) the best system for hacking for hackers by hackers. One could suggest that some of these arguments also apply to vim users accepting an editor rough around the edges and this is partly true but despite the larger number of people who use vim, there seems to be quite different work done there (typically it’s focused only on editing and much less on big complicated useful things like magit/org/calc). Then again I’m pretty sure writing vimscript is a fate worse than death so that is probably why.