Click here to Skip to main content
15,884,537 members
Articles
(untagged)

Why You Must Tame Complexity to Become a Better Programmer

Rate me:
Please Sign up or sign in to vote.
4.53/5 (45 votes)
8 Jun 2015CPOL9 min read 48K   22   27
Why you must tame complexity to become a better programmer

Why you must tame complexity to become a better programmer

Have you ever worked on a system that was just impossible to maintain?

You spend hours trawling through the code until you finally think you understand what’s going on but when you make your change, things fall apart. You introduce ten new bugs in places you thought had nothing to do with the code you changed.

You wade through line after line of code only to discover the method you are trying to understand isn’t being called anymore. It’s dead weight dragging the codebase down.

It feels like you’re seeing double, there are multiple pieces of code that seem to do almost the same thing but not quite. They are 90% the same with a few minor differences.

You are not alone.

This scenario is more the norm than an exception. Luckily, there is an explanation and a way to avoid having your code end up in the same situation.

You need to tame complexity.

Complexity is the root cause of the majority of software problems today. Problems like unreliability, late delivery, lack of security and poor performance. Taming complexity is the most important task for any good programmer.

In the words of Edsger W. Dijkstra:

… we have to keep it crisp, disentangled and simple if we refuse to be crushed by the complexities of our own making.

So why is complexity so dangerous?

To Avoid a Problem, You Must First Understand the Problem.

The more complex a system is, the harder it is to understand. The harder a system is to understand, the more likely you are to introduce more unnecessary complexity.

This is the reason complexity is so dangerous. Every other problem in software development is either a side effect of complexity or only a problem because of complexity.

Complexity has the same impact on your codebase as compound interest has on your credit card balance.

… it is important to emphasis the value of simplicity and elegance, for complexity has a way of compounding difficulties.

-Fernando J. Corbató

How Complexity Impacts Understanding

In a previous post, I took an in depth look at how programmers understand code. For the purposes of this discussion, we can simplify this to two broad approaches. Testing and informal reasoning. Both are useful but both have limits.

Complexity Makes Testing Less Effective

With testing, you try to understand the code from the outside. You observe how the system behaves under certain conditions and make assumptions based on that.

The problem with testing is that all it tells you is how a system acts under the conditions you tested. It doesn’t say anything about how it would act under different conditions.

The more complex a system, the more potential states it might be in. The more potential states a system has, the more tests you need. Unfortunately, you can never have enough tests.

…testing is hopelessly inadequate… (it) can be used very effectively to show the presence of bugs but never to show their absence.

-Edsger W. Dijkstra

Complexity Makes Informal Reasoning More Difficult

When using reasoning, you try to understand the system from the inside. By using the extra information available, you are able to form a more accurate understanding of the program.

If you have a more accurate understanding of the program, you are better able to foresee and avoid potential problems.

The more complex a system, the more difficult it becomes to hold all that complexity in your mind and make well informed decisions.

While improvements in testing will lead to more errors being detected, improvements in reasoning will lead to less errors being created.

The Three Main Causes of Complexity

Before we can avoid complexity, we need to understand what creates it.

Some complexity is just inherent in the problem you are trying to solve. Complex business rules for example. Other complexity is accidental and not inherent in the problem.

Your aim as a programmer is to keep accidental complexity to an absolute minimum. A program should be as simple as possible given the requirements.

The three main causes of accidental complexity are: state, control flow and code volume.

State

One of the first things my computer science teacher taught us in high school was to avoid global variables like the plague. They would cause endless bugs. As Forrest Gump would say:

A global variable is like a box of chocolates, you never know what you’re gonna get.

As you reduce the scope of a variable, you reduce the damage it can do but you never really make the problem go away. This is why pure functional languages like Haskell don’t allow any state.

State Makes Programs Hard to Test.

Tests tell you about the behaviour of a system in one state and nothing at all about its behaviour in an another state.

The more states you have, the more tests you need.

Even if you cover all the possible states (which is virtually impossible for any reasonably sized project), you are relying on the fact that the system will always act the same way given a set of inputs regardless of the hidden internal state of that system. If you have ever tried testing a system with even a tiny bit of concurrency, you know how dangerous this assumption can be.

State makes programs hard to understand.

Thinking about a program involves a case by case mental simulation of the behaviour of the system.

Since the total possible state grows exponentially (the total number of inputs raised to the power of their possible individual states), this mental process buckles very quickly.

The other problem is that if a procedure that is stateless uses any other procedure that is stateful, it also becomes stateful. In this way, state contaminates the rest of your program once it creeps in. There is an old proverb that says, “If you let the camel’s nose into your tent, the rest of him is sure to follow”.

Beware of the camel’s nose.

Control Flow

Control flow is any code that determines the order in which things happen.

In any system things happen and they must happen is a specific order so at some point this must be relevant to somebody.

The problem with control flow is that it forces you to care not just what a system does but also how it does it. In most languages, this is a fairly tricky problem to solve as order is implicit.

Functional languages are slightly better at hiding exactly what is being done than pure imperative languages.

Compare a map function in a functional language to an explicit foreach loop. With the former, you just need to know what “map” does, with the latter you need to inspect the loop, and figure out that it is creating a new set of values from an old set.

Since we mostly work in languages where control flow is implicit, we need to learn a few tricks to limit its impact on our code. Unfortunately, these tricks are outside the scope of this article. I’ll spend some time covering this topic at length in a future article.

Code Volume

This is the easiest cause of complexity to measure.

Code volume is often just a side effect of the previous two problems. It’s worth discussing on its own because it has such a compounding effect. The complexity of a system grows exponentially with the size of the code base and the bigger the code base, the more complex the system.

This interaction quickly spirals out of control so it’s vital to keep a tight grip on code volume.

Secondary Causes of Complexity

Besides the three main causes discussed, there are a variety of secondary causes of complexity that include:

  • Duplicated code
  • Dead code (unused code)
  • Missing abstractions
  • Unnecessary abstraction
  • Poor modularity
  • Missing documentation

This list can go on and on but it can be summarized with these three principles:

Complexity Breeds Complexity

Complexity is so insidious because it multiplies. There are a whole host of secondary causes of complexity that are introduced simply because the system is already complex.

Code duplication is a prime example. The more code you have, the harder it is to know every piece of functionality in the system. Often, duplication is introduced simply because you forget or never knew a piece of code already exists that does what you need.

Even if you know there is a piece of code that does something similar to what you want to do, you are often not sure if it does exactly what you want. When there is time pressure and the code is complex enough that it would take significant effort to understand, there is a huge incentive to duplicate.

Simplicity is Hard

It often takes significant effort to achieve simplicity.

The first solution is hardly ever the simplest. It takes effort to understand the problem deeply and try a number of approaches until you find the simplest possible way to solve it.

This is hard to do, especially if there is existing complexity or there is time pressure. Luckily, we never have to work with legacy code under unreasonable time pressures (note the sarcasm).

Simplicity can only be achieved if it is recognized, sought and prized.

Getting non technical stakeholders to understand this is difficult; especially since the cost of neglecting simplicity is only paid further down the line.

Power Corrupts

If the language you use gives you the option to do something that introduces complexity at some point, you will be tempted to do it.

In the absence of language enforced constraints like Haskell’s immutability, mistakes and abuses can and will happen.

Garbage collection is a good example where power is traded for simplicity. In garbage collected languages, you loose the power of manual memory management. You give up explicit control of how and when memory is allocated. In return, memory leaks become a less common issue.

In the end, the more powerful a language is, the harder it is to understand systems constructed in it.

Simplicity is Not Optional

Unnecessary complexity is a dangerous thing.

Once it creeps into your solution, it grows like a cancer. Over time, it strangles your ability to understand, change and maintain your code.

Every decision you make should prioritize simplicity. Learn to recognize complexity when you see it. Strive to find a simpler solution. Value clear code over complex solutions.

Your mission is to pursue simplicity at all costs.

Become a Better Programmer Today

Get Your Free eBook

Becoming a better programmer isn’t easy. There is a ton of information on the internet that teaches you the individual technologies you need to know to be a programmer. There aren't many places that focus on the underlying principles that make you a good programmer regardless of the technology you use.

In this free eBook, you will learn about these principles and how you can use them to become a better programmer.

SEND MY FREE EBOOK

References: Out of the Tarpit, Ben Moseley & Peter Marks

The post Why You Must Tame Complexity to Become a Better Programmer appeared first on Better Programmer.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Allan Gray
South Africa South Africa
Since I was young I always preferred teaching myself to learning from a structured syllabus.

This didn’t always make me popular with my teachers. I guess sleeping through the whole of Romeo and Juliet in an English class doesn’t make a great impression. On the other hand I formed an obsession with always researching something.

Over the years these obsessions would jump from topic to topic ranging from physics to lean manufacturing. Photography to functional programming. Cognition to physiology and strength training. Kitesurfing to fire poi. My obsessions may be random but are always led by my curiosity.

Over time I noticed that I was forming connections and finding patterns in unrelated fields and using knowledge I gained from studying the rudiments of things like cognitive psychology when trying to figure out why I was seeing certain behaviours in the teams I was working in.

I'm interested in communicating those underlying principles and connections in a way that is useful to others.

I have been working as a programmer, architect and manager for the last 12 years so there is a strong bias to helping junior programmers and software development teams improve.

I focus on underlying principles and not technologies.

Comments and Discussions

 
QuestionNo e-book and nothing new in this article anyway! Pin
User 324245828-Jul-15 2:59
professionalUser 324245828-Jul-15 2:59 
GeneralMy vote of 3 Pin
dmjm-h27-Jul-15 8:26
dmjm-h27-Jul-15 8:26 
QuestionGreat article Pin
Daniel Miller27-Jul-15 8:03
professionalDaniel Miller27-Jul-15 8:03 
Questiona bit ambiguous ... Pin
peterkmx13-Jul-15 21:47
professionalpeterkmx13-Jul-15 21:47 
AnswerWell thought !... but Pin
Liju Sankar12-Jul-15 8:48
professionalLiju Sankar12-Jul-15 8:48 
GeneralMy vote of 1 Pin
Mark Knell29-Jun-15 7:51
Mark Knell29-Jun-15 7:51 
QuestionJust theory show me the code.... Pin
Shivprasad koirala22-Jun-15 23:00
Shivprasad koirala22-Jun-15 23:00 
Its nice but its just theory. Examples would have been great.
My book .NET interview questions with 500 mostly asked questions in .NET world .NET Interview questions and answers

QuestionGreate Pin
Troy Bryant10-Jun-15 16:37
Troy Bryant10-Jun-15 16:37 
QuestionMy vote of 5! Pin
jediYL9-Jun-15 17:51
professionaljediYL9-Jun-15 17:51 
QuestionUnderstanding Programs Pin
Member 103687539-Jun-15 14:48
Member 103687539-Jun-15 14:48 
AnswerVery well written! Pin
Member 109422619-Jun-15 10:33
Member 109422619-Jun-15 10:33 
GeneralRe: Very well written! Pin
rinzai9-Jun-15 11:38
rinzai9-Jun-15 11:38 
GeneralRe: Very well written! Pin
Member 256047229-Jun-15 0:25
Member 256047229-Jun-15 0:25 
GeneralMy vote of 5 Pin
Frank T. Clark9-Jun-15 9:25
professionalFrank T. Clark9-Jun-15 9:25 
GeneralGood article Pin
Hrln.Mrshll9-Jun-15 8:40
Hrln.Mrshll9-Jun-15 8:40 
QuestionInteresting Article - But .... Pin
Member 113151849-Jun-15 8:10
Member 113151849-Jun-15 8:10 
AnswerRe: Interesting Article - But .... Pin
Biel Simon9-Jun-15 9:09
Biel Simon9-Jun-15 9:09 
AnswerRe: Interesting Article - But .... Pin
Frank T. Clark9-Jun-15 9:33
professionalFrank T. Clark9-Jun-15 9:33 
GeneralRe: Interesting Article - But .... Pin
Dewey9-Jun-15 10:34
Dewey9-Jun-15 10:34 
GeneralRe: Interesting Article - But .... Pin
rinzai9-Jun-15 11:37
rinzai9-Jun-15 11:37 
GeneralRe: Interesting Article - But .... Pin
Frank T. Clark10-Jun-15 2:04
professionalFrank T. Clark10-Jun-15 2:04 
AnswerRe: Interesting Article - But .... Pin
jediYL9-Jun-15 18:08
professionaljediYL9-Jun-15 18:08 
GeneralRe: Interesting Article - But .... Pin
Member 1131518410-Jun-15 4:53
Member 1131518410-Jun-15 4:53 
QuestionPowerful languages != complexity Pin
SomewhatConservative9-Jun-15 8:10
professionalSomewhatConservative9-Jun-15 8:10 
QuestionVery good! Pin
Sander Rossel9-Jun-15 2:16
professionalSander Rossel9-Jun-15 2:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.