Q: What's wrong with complexity?
A: It hides bugs!!
Notwithstanding that some tasks are inherently complicated and there's just no simple way to express it I would posit that the problem with unnecessary complexity is that it hides bugs.
Bugs hide in the edges. They swarm in corners. They positively thrive in interfaces.
Simple (i.e. clean) code helps make the bugs stick out.
There is some business case or need behind each programming effort. That case/need has an underlying language, a grammar, that embodies what needs to be done and when and how. The challenge in software development is to find programming constructs which, inasmuch as is possible, clearly expresses the problem's solution.
Through hard-won experience we've discovered things that suggest where bugs like to hide; what are sometimes called "Code Smells." Massive, nested IF statements. GOTO statements. Global variables. Side effects.
But, these are only symptomatic. There are times when some of these ARE necessary to achieve maximum performance. In other words, not all cases are bad, but they suggest a long, hard look to ensure they are necessary.
Consider programming languages. We started with machine code. Then we wrote assemblers to make it easier to express what we wanted to do. Then came macros and functions. Still later we developed higher level languages. All of these steps allowed us to more easily express what we were trying to do.
Consider this continuum. Case 1: A single program with thousands of lines of code and no functions or subroutines. Case 2: The same application implemented with thousands of functions, each of which contain at most 5 lines of code.
There's probably a sweet spot in between those extremes. A point where the abstractions of what needs to be done closely mimics the problem domain. Where the inputs and outputs are clearly delineated and checked. Where each function is the "right size". As the lowest level functions are implemented and tested, they provide a framework, a language in which one can more easily express what is being done. It is clear what is attempted and how. And it is clear when things are amiss. It makes the bugs "stick out."
When we fail to do this, when we unnecessarily complicate the code, then problems arise. We struggle to grok the code. In this struggle, overwhelmed in trying to follow the "good" paths, we fail to see all its shortcomings. We provide hiding places for bugs. They blend in. They avoid observation. Or, when detected, resist eradication. For some samples, take a look here.
tl;dr. Given: Some problems are just plain complicated and defy any further simplification. Fine. Other problems are amenable to consistent abstractions. When we fail to do that, we introduce unnecessary complexity. We breed bugs.