Software project complexity

Same problems can be (and get) solved very differently, and apparently the difference in spent time and effort can sometimes be measured even in orders of magnitude, especially if maintenance of—and interaction with—such solutions is taken into account. Programmers usually agree that simplicity is important, but disagree on simplicity definitions.

There are various software complexity metrics, though they cover little and potentially wrong. There's also plenty of articles which either focus on a particular view of complexity, or just praise general simplicity without getting into details. Here I'd rather try to outline different aspects of software project complexity, and different views on it. Many of these seem to be similar, related, and/or interdependent.

"Simple" vs. "easy"
Confusion of those is both widely known and widespread.
Hardware abstraction
Having no complicated compilers or interpreters between source code and executed machine code can be seen as a kind of simplicity, while having to deal with low-level details is often seen as unnecessary complexity.
Mathematical simplicity
A simple model of computation, possibly along with elegant type system, abstractions, and/or nice applied models can be seen as simple. It can also be seen as an arbitrary and unnecessary obstacle, adding strange cruft to the codebase, and/or not being straightforward.
Complexity of maintenance (and debugging time in particular) depends on correctness, which is often expected to follow from software simplicity; the "simplicity" definition usually varies. Views on other methods of achieving correctness (memory- and type-safety, precise specifications and verification, testing, code reviews, etc) also vary.
Data models
Complexity of data models is a topic I covered in a separate note. Some see it as an important contributor to project/software complexity, some ignore it completely.
Code quality
General code readability, nice and conventional formatting and naming, comments and annotations, documentation, absence of anti-patterns, -Wall (or similar), linting, and various other language-specific niceties can be seen as a kind of simplicity (as in "absence of a mess"). Though not universally: some rebel against conventions, claim that their code is self-documenting, and so on.
Code complexity
Number of lines of code, cyclomatic complexity and friends, lack of abstractions or presence of unnecessary ones, etc. It's a large one, and partially covered by the other listed aspects; it gets a separate entry mostly to highlight that it's just a part of overall project complexity.
Also partially covered by other aspects, but perhaps worth listing separately: a poorly designed system can introduce a lot of accidental complexity. Though views on what a good architecture looks like vary.
Build system
An unconventional (and uncommon) build system can complicate both development by others than the original author, and building by users, often introducing additional dependencies. Though I guess they usually get chosen in the first place since they seem to simplify the task (make it easier, and/or make the configuration simpler).
While they help to reduce effort duplication and code complexity, it can be quite a burden to pull and build dozens or hundreds of those even for a small project: even though the process should be automated, an issue in any of direct or indirect dependencies could break the build, build time increases, dependency tree inspection gets harder.
Standard facilities
Logging, scheduling, access control, authentication, packaging and dependency management, dynamic linking (and library updates), IPC, and other standard tools and facilities are usually available, use of which makes a whole system easier to manage, and can simplify software considerably. But more portable, independent, or self-contained software may be preferred sometimes, and over-reliance on such facilities can indeed complicate a project (e.g., turning it into a pile of shell scripts).
Other standards and conventions
Virtually anything custom complicates interaction, composability, and/or usage/maintenance, and needs to be implemented (hence complicates the code). An argument for custom solutions is that they can fit the task better, while avoiding complexity of generic solutions (and possibly dependencies). Some just leave things non-compliant/broken as long as they work in specific cases where they need them.
Component availability
It simplifies both development and usage/maintenance if the compiler/interpreter and all the dependencies are commonly installed on target systems, or can be installed from standard repositories with low overhead. I don't think that such availability is ever seen as a complication, but can be seen as unimportant or non-critical.
Development process
That's another large and controversial aspect: issue tracking and project management software, various methodologies, communication software/protocols, CI/CD systems and other technologies are supposed to simplify the process, but may seem/be annoying, inefficient, time-consuming, and/or unnecessary as well.
Plenty of complexity can come from potentially changing requirements that don't fit into clean and simple models. But some of those can be discussed and reconsidered, especially if those are solutions rather than problems.
Suitable tools
The law of the instrument is widely known, and it's often said that tools suitable for a task should be used. Suitable tools can greatly simplify solutions, but it's hard to identify those, and merely being aware of this bias doesn't prevent one from counting their favourite tools (or—even worse—whatever is used at some large company, was praised in some recent blog post, or just commonly used) as suitable for everything.

Even though views on those vary, it seems useful at least to pay attention to them, to be aware of those. Many complexity types seem to stay unknown or ignored until one runs into a mess made with those.