For myself, I find the most important thing is to have clear interfaces (contracts). That is, I can write the hackiest code inside of a module, but I will spend time upfront to make sure that what that module exposes is the cleanest I can make it.
Then, I can isolate and fix a function by itself. It may have been written to be 200 lines long, filled with hackery and half measures, but my complexity is contained within it, and the functions it calls out to (so nothing calling it should need to be substantively changed). Those called functions, in turn, may also need improvements, but my focus every step of the way was keeping the interfaces clean, so I can always get down to some base set of functionality that has no dependencies, fix that up, make the necessary fixes to keep things working in the functions directly calling them, test, and then move up to those calling functions and repeat, until I get back up to the big ugly 200 line monstrosity. Every step of the way I can make sure things are still working, and I don't have to substantively touch anything above the 200 line monstrosity until I've cleaned it up.
So by keeping my interfaces clean, I can figure out how, inductively, I can make progress.
The lack of clear interfaces is the real problem; if you don't put in the effort when writing your code to keep those clean, you end up with circular dependencies, implicit workflows and state between functions ("to call X you first have to call Y to get a foo, pass that into X, then call Z to reset the foo value"), and other nightmares, that move you more to needing a full rewrite.
Also, plain old data interfaces are very desirable. They decrease coupling. Get the data off the interface and restructure it (renaming, preprocessing...) in a suitable way before implementing the actual functionality. This decrease in coupling makes representation changes in the interface more practiceable.
Of course sometimes stateful (procedural) interfaces are a must, but it's surprising how many painful OOP classes can be replaced with a "const struct foo" interface with a clear meaning to the data in it.
Higher order functions are one tool to help transform procedural interfaces into data interfaces---after all that's the whole point of treating functions as data.
What exactly is "the whole point of treating functions as data"?
Of course you can declare everything is data. You don't need higher order functions for that. But real data is simple and introspectable. Computation is not.
Yes, even pure functions are not introspectable. All you can really do with a function is call it on a value and get a return value. (For the sake of simplicity, I am ignoring side effects here.)
Let me try to rephrase with an example. Eg you might have an API for a file that allows you to open a file, manipulate it, and then close it. That's a very procedural interface.
As an alternative, think of an interface like the following:
withOpenFile(filename, manipulator)
that opens a the file, calls the manipulator function on the contents, and automatically closes it.
Or compare map, filter and reduce vs manually iterating over a collection of items.
nitpick: in Javascript you can actually call functionName.toString() and get the implementation. AFAIK angular did this trick to implement their dependency injection mechanism.
My experience has been that shitty code tends to be shitty because it accesses "magic global state," so it's hard to coral it into a single module almost by definition. This happens usually because of rushed deadlines; it's often hard to plumb through an extra parameter if there are many touchpoints or a lot of tests to fix, and easier to just stuff something into a global variable and ship the code.
Then, I can isolate and fix a function by itself. It may have been written to be 200 lines long, filled with hackery and half measures, but my complexity is contained within it, and the functions it calls out to (so nothing calling it should need to be substantively changed). Those called functions, in turn, may also need improvements, but my focus every step of the way was keeping the interfaces clean, so I can always get down to some base set of functionality that has no dependencies, fix that up, make the necessary fixes to keep things working in the functions directly calling them, test, and then move up to those calling functions and repeat, until I get back up to the big ugly 200 line monstrosity. Every step of the way I can make sure things are still working, and I don't have to substantively touch anything above the 200 line monstrosity until I've cleaned it up.
So by keeping my interfaces clean, I can figure out how, inductively, I can make progress.
The lack of clear interfaces is the real problem; if you don't put in the effort when writing your code to keep those clean, you end up with circular dependencies, implicit workflows and state between functions ("to call X you first have to call Y to get a foo, pass that into X, then call Z to reset the foo value"), and other nightmares, that move you more to needing a full rewrite.