Some Thoughts on Software Complexity

Some Thoughts on Software Complexity

Last updated:
Table of Contents

If you spend most of your time coding Information Systems and if you develop software within an MVC framework/mindset, the most stable, clean and reliable parts of your application are your database tables.

We usually build our applications around these datatypes. We model our domain classes after the database (this is very likely the case if you're using a design pattern like Active Record), and we reason about our domain over a cup of coffee with our database diagram in our hands.

We can't escape complexity. We can manage it and control it and understand it, but we can't escape it because we are modeling complex behaviour and processes.

There are two types of complexity:

Inherent vs Accidental Complexity

  • There's inherent complexity in all software systems which we cannot avoid and, without which, our application is basically worthless. Only complex problems are worth solving. Software systems need to be complex in order to justify their existence. You can't avoid this.

  • There's accidental complexity too, which is complexity that doesn't add any extra value to your users. Under this category we can put things like bloated code, duplicated behaviour, over-generalized classes, large methods and so on. This is unnecessarily complex code that will make your software harder to change and extend, and won't you get you any extra money. These are things which can (and should) be understood and tackled.

Where Complexity Builds up

Models

It is just too easy to add a method to a model class. This may well be the most serious form of complexity creeping into your system, as it is also the most difficult to change because its changes will affect the whole application.

If this becomes too unwieldy to maintain and extend, you're probably looking at a major overhaul ($$ and time) that will probably affect and/or break your controller too (probably not your views though).

Controllers

It is not so bad to add many actions (that is, separate routes that each render a separate view) to a controller; this is the way it's supposed to be.

Views

Views tend to get very complex and bloated when you try to do too much in a single view/template (I'm talking mainly about views where some sort of interactive action takes place, i.e. forms basically).

New models make themselves needed, unused models die out throughout the lifetime of an application.

Models always change through the lifetime of an application: new attributes (and models themselves) make themselves needed, unused attributes and models tend to die out as new, more effective ways to model your domain are figured out. This means that your views need to be constantly updated to match the moving targets which are your models.

From my experience, it's better to have many simple views than a just a few with a lot of functionality each, as they are much easier to keep track of. You can always refactor common code that turns up across different views.

Helpers

We generally don't pay as much attention to these as to the main code in our application (models mainly), as they are seen as some sort of second-class citizens. Their main objective is just to support the application and extract code that is not specific to our domain from our app.

Tests

Needless to say. Not many of us take part in projects where the cleanliness and organization of testing code becomes a problem.

Ways to Mitigate it

Models

Extract code to helper classes and methods if it starts to get unwieldy.

When you first need to do something with a model (for example in a controller in order to select some data to display) somewhere, write that code there (where it's actually used). If it's more than, say, 20 lines or so or if it is needed by other parts of your application, extract to code to a helper method. Only if the helper method becomes too large and overgeneralized should you move this code to the actual model class.

Controllers

Extract code to models if there is too much logic in the controllers (controllers must be thin and dumb)

Views

Use helpers or pass information down from the controller in a more digested form–so that the view doesn't need to have as much logic.

Helpers

Helpers are code too!

We can't fall prey to the notion that helper classes are just somewhere we put ugly, ad hoc code we are ashamed of.

We never know how big our helpers will turn out to be so it's difficult to think up a clear solution up front.

One helper per controller - each controller only talks to its own helper!

What could be done is to create one helper per controller, and use only those in each controller. If your controllers usually map to a domain class, extract all ad-hoc code to a helper that's exclusive to that controller. If you need access to outside classes, keep those calls in your helper–and your controllers clean.

Tests

Same old: refactor into functions and extract to helpers if needed.