My experience with JenkinsFile has improved once we setup library functions for it to leverage on. Only downside is that you need to write them in Groovy but the payoff is that the JenkinsFile reduces to basically a function call or two per stage which makes it super clean + it makes JenkinsFiles more or less "standard" (because there is practically no project-specific stuff in it - the complexity is managed by the library functions). This approach has made fixing issues or enhancements so much more easier for us.
At the end of the day, its just the shell executing the commands :) sure, you can always distil it down to a script (or a set of scripts) and nothing wrong with it either - as always, depends on the context.