Managing Complexity

Software development is difficult. Especially if you are working on a 25-year old enterprise software that has gone through multiple platforms, technologies, and has seen multiple generations of developers. All of which makes it hard to understand and difficult to develop. What can we do as developers? Turns out, a lot.

As a developer, I spend a lot of time writing code. But I spend even more time maintaining that code. Often, I go back to the code I wrote just a week ago and find it a tangled mess that I almost cannot understand. Over time, entropy kicks in, and the code becomes so complex that it’s hard to even know what was I thinking.

In his landmark paper titled, No Silver Bullets, Fred Brooks describes two types of complexities which make software development difficult. Essential, and Accidental.

  1. Essential complexity: Complexity inherent to the software. Properties that a software must have in order to be that software. This includes a complex domain and complicated business logic.
  2. Accidental complexity: Complexity not related to the software. Properties that a software just happens to have, which don’t really bear on whether the software is what it is. The software could very well do without these.

In Code Complete, Steve McConnell argues that Managing Complexity is the most important technical topic in software development, making it the software’s primary technical imperative. The book essentially provides a two-part approach to managing complexity:

Minimize the amount of essential complexity that a developer’s brain has to deal with at any one time. Keep accidental complexity from needlessly proliferating.

Based on these principles, there are quite a few techniques we can use to reduce complexity.

  1. Isolate: As software developers, we shouldn’t try to cram whole programs into our heads at once. We should try to organize our programs in such a way that we can safely focus on one part of it at a time. The goal is to minimize the amount of a program you have to think about at any one time.
  2. Divide and Conquer: The complexity of a problem is reduced by dividing the system into subsystems. The goal of a software design technique is to break a complicated problem into simple pieces. The more independent the subsystems are, the more we make it safe to focus on one thing at a time.
  3. Abstractions: Writing programs in terms of the problem domain, rather than in terms of low-level implementation details, and working at the highest level of abstraction reduce the load on your brain.
  4. Simplify: Avoid making clever designs. They are hard to understand. Instead make ‘simple’ and ‘easy-to-understand’ designs. If your design doesn’t let you safely ignore most other parts of the program when you are immersed in one specific part, the design isn’t doing its job.
  5. Reusability: Designing the system so that you can reuse pieces of it in other systems.
  6. Non-leaky Abstraction: Trying to build the software in such a way so that you can view the system at any single level and get a consistent view. Design the system so that we can observe it at one level without dipping into other levels.
  7. Information Hiding: Asking ‘what details and information should I hide?’ solves many difficult design issues.
  8. Programming Tools: An effective use of variables, routines, classes and packages provided by the programming language can help reduce complexity tremendously. Design is needed at several different levels of detail, as follows:

From: Code Complete

I guess the many challenges involved in building a working software are what makes the activity so much fun, and a worthwhile pursuit to follow. If it was easy, it wouldn’t be fun, either. So I guess I am better off embracing and managing all this complexity. Happy coding!

Written on March 29, 2019