Seeing Complexity Like an Engineer
- domingo

- Dec 15, 2025
- 3 min read
This quarter in class I learned a lot more about complexity in systems. One of the points where my classmates and I kept getting stuck was on complexity itself. When re-designing system architectures, we almost universally commented that if you are designing a system with efficient decoupling in mind, you are also always increasing the complexity of the system. This baffled our professor, who would correct us, saying that if you are paying attention to the separation of concerns, then it should almost always reduce complexity of a system.
Our professor is an industry professional, and is thinking of software from a systems-lifecycle perspective. He cares about whether the engineers that work under and after him have an easy and efficient codebase to maintain and extend. As students, we were thinking from an onboarding and learning perspective, where the size and number of classes may heavily impact how quickly we can start doing our jobs effectively. We were talking past each other. Complexity is not a single property of a system, but a function of who is interacting with it and for how long.

How Students Experience Complexity
I took some time to think about this disparity more, and decided that we were defining complexity like students, not like industry professionals. To us, a system’s complexity can be defined by how difficult its repository is to navigate. If we have to wade through ten layers of interfaces in order to find implementation code, then a system is complex.
We spend so much time learning how to traverse novel codebases that we have (perhaps not incorrectly) identified learning a codebase as the primary obstacle to our learning. When abstractions multiply, the immediate experience is friction: more files, more indirection, and more time before we feel productive.
“Life is really simple, but we insist on making it complicated." - Confucius
How Professionals Experience Complexity
However, full-time engineers may spend years with a single codebase, and know it well after the first few weeks of working in it. The difficulty then is no longer navigation, but understanding. The problem shifts toward writing and maintaining self-documenting code.
Complexity to the professional is measured by how easy it is to understand the purpose of older code, how easy it is to reuse and test that code, and how easily it can be extended in new ways without breaking existing behavior. From this perspective, decoupling and separation of concerns reduce complexity by isolating change and preserving intent over time.
Tradeoffs: Accidental vs. Essential Complexity
This goes deeper into the tradeoffs we discuss as professionals. There is an amount of accidental complexity introduced when we design for long-term efficiency, and the immediate cost is often codebase size. We accept additional layers, interfaces, and abstractions that may not be strictly necessary in the short term. In return, we gain essential complexity: the ability to perform core business processes, reason about system behavior, and expand functionality safely over time. There is also a cognitive tradeoff at play. These systems are more difficult to learn up front and have a higher learning curve, but they are easier for multiple teams to maintain, reason about, and evolve over the long run.

Looking Beyond Algorithms
As students, we are tasked with looking beyond algorithms and into the bones of our codebases. The important questions are no longer just how fast does this run, but what contracts exist within this system. What foundational boundaries have been established to make the system more testable, maintainable, and understandable?
Learning to see these tradeoffs we recognize when complexity is being shifted rather than eliminated. This is part of the transition from student thinking to professional thinking.
Ultimately, the tension we experienced in class was not about right versus wrong design, but about perspective and experience. Decoupling, abstraction, and separation of concerns do not eliminate complexity; they relocate it. What feels like arbitrary abstraction to a student can become clarity and extensibility to an engineer maintaining that system years later. Learning to recognize this shift is part of growing as a software engineer. As students, our challenge is to move beyond optimizing for the short term and begin asking how our systems will behave under change. Who will read this code next and how resilient will it be when it is inevitably challenged?





Comments