I think the most exciting trend in web development is the rise in popularity of functional programming styles in front-end frameworks.
This makes the complexity problem much easier to solve, as the code is (should be) less likely to cause an unanticipated mutated state which can't be easily tested for.
I remember this same "rise in popularity" in 2005. Every few years functional advocates get all excited (last time was F# support in Visual Studio, all C# programmers were going to switch, naturally).
I suspect the reality is a small subset of programmers think functional programming is amazing and everyone else hates it. You might think it reduces complexity, but a lot of people feel it reduces comprehensibility.
> I suspect the reality is a small subset of programmers think functional programming is amazing and everyone else hates it.
Yep. It's not that I hate it, I just don't like it. The thing is that the functional-praisers are much more vocal about how they love it whereas people who write imperative do not care much about Haskell.
We are happy with LINQ and that's all 99% of us wants/needs.
Would you mind sharing an example or pointing me to a good guide that explains this concept? How does functional programming make a problem less complex than OO or imperative? I've heard this a couple of times, but the intuition has never quite clicked for me.
Its state. Functional programs tend to have less state as their output is the same for some given input. With things like jquery you quickly introduce state, say is some dropdown open, which your next function will have to check is true or false before proceeding. And so on.
I'll give it a shot; functional programming style--some languages enforce it, some languages merely have features that allow for it if the author is disciplined enough to do so (JavaScript is in the latter camp)--generally eschews mutable state and side effects, i.e. a variable `foo` that is declared outside of the function cannot be altered by the function. Some "pure" functional languages restrict all functions to a single argument. This may feel like an unnecessary constraint (and opinions vary), but one thing that can't be denied is that it keeps your methods small and simple; in any case, it can be worked around by applying a technique called "Currying"[1], which is named after the mathematician & logician Haskell Curry, not the dish (also the namesake of the eponymous functional programming language [the mathematician, not the dish]).
Because nothing outside of the function can be changed, and dependencies are always provided as function arguments, the resulting code is extremely predictable and easy to test, and in some cases your program can be mathematically proven correct (albeit with a lot of extra work). Dependency injection, mocks, etc are trivial to implement since they are passed directly to the function, instead of requiring long and convoluted "helper" classes to change the environment to test a function with many side effects and global dependencies. This can lead to functions with an excessively long list of parameters, but it's still a net win in my opinion (this can also be mitigated by Currying).
A side-effect (hah) of this ruleset is that your code will tend to have many small, simple, and easy to test methods with a single responsibility; contrast this with long and monolithic methods with many responsibilities, lots of unpredictable side effects that change the behavior of the function depending on the state of the program in its entirety, and which span dozens or hundreds of lines. Which would you rather debug and write tests for? Tangentially, this is why I hate Wordpress; the entire codebase is structured around the use of side-effects and global variables that are impossible to predict ahead of runtime.
There is much, much more to functional programming (see Monads[2] and Combinators[3]), but if you don't take away anything else, at least try to enforce the no-side-effects rule. A function without side-effects is deterministic; i.e. it will always give you the same output for any given set of inputs (idempotence comes for free). Because everything is a function, functions are first-class citizens, and there are only a few simple data structures, it becomes easy to chain transformations and combine them by applying some of the arguments ahead of time. Generally you will end up with many generalized functions which can be composed to do anything you require without writing a new function for a specific task, thus keeping your codebase small and efficient. It's possible to write ugly functional code, and it's possible to write beautiful and efficient object-oriented code, but the stricter rules of functional style theoretically make the codebase less likely to devolve into incomprehensible spaghetti.
Manning Publications has a book[4] on functional programming in javascript, which I own but haven't gotten around to reading yet, so I can't vouch for it. However, it does seem highly applicable.
This makes the complexity problem much easier to solve, as the code is (should be) less likely to cause an unanticipated mutated state which can't be easily tested for.