A few important qualities that make it particularly good for this sort of environment:
1. Dynamic scope. Dynamic scope (aka special variables) means that if I locally bind the variable foo to bar, that binding is available for the dynamic extent of the let (ie a function deep in the call stack may access that binding of foo even if it was not defined lexically inside the binding). This allows passing state or options or configuration between different places which are amenable to extension, or modifying the behaviour of existing features by temporarily modifying their state. In other systems these problems may be solved by using lots of arguments, special mechanisms for passing custom parameters around, or well defined protocols to extensions with their own state. In those alternatives the fallback is either to impossibility or to storing state in poorly managed global variables. Elisp handles that “local global” state better and eg makes sure it correctly deals with multiple bindings or stack unwindings. One example usage would be for some custom comment syntax. You can just (locally) bind custom values to the variables which define the comment syntax and then call the usual comment function.
2. Hooks are very natural data structures which extensions use to make explicit extension points. Because books are lists, they may be conveniently dynamically bound (e.g. (let ((foo-hook (cons blah foo-hook))) ...)).
3. Buffer-local variables attach arbitrary state to each buffer and make it available as global variable bindings when that buffer is current. The alternative would be less begged local state, state attached to many more different things, and doing lots of requests for current-buffer.state.foo. Maybe these would make you think more about the current buffer changing but I’m not sure thinking about the problems leads to less buggy code. Buffer local variables makes writing modes easier and it makes it easy to not have to care much about the existence of other buffers while still easily being able to write code that spans multiple buffers.
4. Advice. Advice is like complex hooks except you can put it almost anywhere. You can “advise” any function with extra code to run in various places (eg before, after, around, just modifying the arguments, before but the advice is and’d with the function so if it returns nil the function isn’t called, and so on). This lets you tweak just about anything. For example I was using a build-managing mode which was not so extensible but by advising process creation I could modify its behaviour to nice the compiler executable.
5. Various save-restore macros. This could be done with first class functions but programming against the macros makes common idioms so much easier. Maybe an argument could be made that not having global state would make things a lot more manageable but I think macros like save-excursion, with-temp-buffer, and so on generally keep things under control
6. Most state is globally accessible. By this I mean most variables are special, and there is lots of important state which is global or buffer local like the point, mark, window configuration, or current buffer. This means that the current state is generally available to any extension code that runs and not kept private to some other bit of code (prohibiting extensions from getting at it). It allows most behaviour to be tweaked.
7. Recursive editing. It’s just really nice that you can pause half way through your program, have some editing done, then resume where you were: the user may live inside as well as outside the elisp call stack. Though mostly this is infrequently used.
8. Simple data structures. Because most things are text in buffers, you generally move around this data structure in a rough way (search forward for this, edit that, and so on) which is resilient to that data structure being changed/extended under you.
It’s curious that a lot of these qualities are generally considered terrible features for writing maintainable code. This is because maintainable code generally wants different things to not be able to affect one another much. If you want an extensible, malleable system, it probably makes sense to have things which are easy to modify the behaviour of from just about anywhere.