At CodeMash v126.96.36.199 I attended a great session by Cory House (bitnative.com), "Pragmatic Architecture in .NET: Curing the Architecture Astronaut." I'd never heard the term "Pragmatic Architecture" before, but it really resonated with me—and I feel that it accurately describes my own approach to software architecture.
The session had an interesting quote: "Complexity [in architecture/code] is neither good nor bad, but it must be justified." I like that, but I'd go a step further and say that complexity is always bad, but sometimes it's necessary. That harkens back to one of my favorite Albert Einstein quotes: "Make things as simple as possible, but not simpler."
So what is complexity? And before we run it out of town like an angry mob, is it really bad? Sometimes complexity is really easy to spot; a very tangled entity diagram, a method with ugly code that is inscrutable even to the author, methods or files running into thousands of lines, code duplicated in multiple places. To my mind, complexity is also much smaller than the obvious examples. Everything you add to a project increases complexity. Each line of code increases complexity in some small way. Each comment, likewise. Each class or file or method or external dependency makes the project "bigger" to understand. Refactoring code to reduce lines, reusing code or removing dead code can reduce complexity. I say "can" because fewer lines of complex code might be harder to understand than more lines of simple code.
How do we benefit from simplicity? Simpler/smaller code bases are easier to understand—which helps new developers spin up faster and makes existing developers more productive. Fewer lines of code equates generally to fewer bugs. Systems with simpler interfaces have smaller attack surfaces. Fewer modules interacting means fewer places for things to break.
These ideas translate pretty directly to the software architecture of a system. Layers, frameworks, interdependencies and APIs are all things that increase complexity whether justified or not. Is it justified to put your Data Access Layer (DAL) behind a Web service rather than use direct library calls? Does your Object/Relational Mapping (ORM) for your persistence layer decrease complexity or does it make it worse?
One good way to evaluate your architecture is to see how hard it is to (for example) add a new field to an existing database table and add it as an editable field in your UI. Do you need to touch 3 files? 5 files? 18 files in 5 separate projects?
There are several common arguments in favor of complexity. One is simple religious dogmatism: "That's the way we do things here." You can't argue with programming fanatics any more easily than other fanatics, so I won't bother. My own view, based on my experience in contract development, is that there's no room to be dogmatic in software development. (As an aside there's one and only one thing that I'm completely dogmatic about in programming: it's not OOP, Agile, or any other paradigms. It's the absolute requirement to use source code control. Anything else is debatable.)
Another argument in favor of complexity is, "We might need it someday." This is essentially a gamble, and frankly I've never guessed right about what would be needed down the road—and neither have any of my clients! A simple system is easier to refactor and expand down the road, and the only thing you can say for sure about a complex system that you "might need" is that you've paid the cost for it upfront, and will continue paying for it with each new feature that touches on that complexity.
In architecture I'm always going back to the common idiom of "Do the simplest thing that meets the requirements" (with quality, maintenance and development speed being part of the requirements). Not every project needs an ORM, or a DAL, or even a layered architecture. Web services, while really cool, are not always necessary for a successful project. Don't default to "Enterprise Architecture" unless what you are building really requires it. It's much easier to add complexity to a system that is too simple than it is to remove complexity from an over-architected system.
And one final example from the "Pragmatic Architecture" session: "Anyone can build a bridge that stands, but it takes an engineer to build a bridge that barely stands." Similarly, any programmer can design a system, sometimes by over-architecting it and creating unjustified complexity. It takes a true architect to design a system so simple and minimal that it barely works. And like the engineer's bridge, that system would be far cheaper to build and maintain.