Build maintainable software with an engineering mindset
What is maintainable software?
I previously made the case that we can list the outcomes of software engineering in this way:
The software design should:
These are 4 simple goals software engineers can use as their “true north”. Software engineers do much more than just coding, but when we do write code, these goals can help us ensure the code we are producing serves the business over the long term.
The first goal is basically: “Write code that works.”
It’s quite easy to figure out if code doesn’t meet user or business requirements. We have all manner of testing to do this.
We can summarize the next 3 goals as: “Write maintainable code”.
This is where we software engineers come unstuck.
Time's the problem
The obstacle to writing quality, maintainable code is that “maintainability” can only be decided in the future. A developer writing code today, can only speak to the maintainability of the code sometime in the future, after they have fixed multiple bugs, and implemented multiple new features.
The first goal - writing software that works - can be measured now. Writing maintainable software can only be measured later.
How do you improve the quality of your code today, when you will only receive feedback in the future?
领英推荐
The engineering mindset
Of course, the time dimension of identifying maintainability is not unique to software. The problem arises any time we build something that is going to be around for a long time. A civil engineer has the same problem in designing a bridge to be cost-effective over its lifespan. It’s not just minimizing initial building cost that’s important, but the total cost of the bridge over its useful lifespan.
This is not a science – it’s a matter of judgement. It requires simultaneously having a very high, and low level, view of the problem, and optimizing for multiple variables.
But we know how to optimize for multiple variables – so we can calculate this, right? Machine learning to the rescue?
No, because the crucial part of the problem is that we’re optimizing for multiple future variables. We don’t know how the business will require the software to evolve over time. We’re making educated guesses of future events that could play out, balancing all those risks with their mitigations, and translating all of that into code in the present.
An engineer doesn’t just make these tradeoffs with absolute confidence. There is an understanding that all these estimates have a compounding error, and the future is unknown. That this is just our opinion.
So, there is high value placed on design choices that improve optionality, over choices that decrease optionality. We generally prefer to make choices that leave open the possibility of reversing the decision or adjusting it later. Optionality is the opposite of painting yourself into a corner.
That’s what software engineering, like other fields of engineering, is about. We’re not just concerned with writing something that works today.
An important trait of an engineering mindset is reflection. This is the ability to look at your work dispassionately, like a stranger did it, and see its flaws. You’d normally do this before your software was released. After it’s released, and the bugs start coming in, you have to look at your code and identify the root cause of every bug – the true root cause – to ensure you don’t make the mistake again.
Reflection
The true root cause is not that this reference was null, or that exception was unhandled. The true root cause is to understand why your mental model of programming allowed such a bug to be written.
The next step, the permanent countermeasure, is to adjust your mental model of programming so the bug doesn’t happen again.
Disclosure:
I am a Software Engineering manager at SYSPRO. Any views expressed in this article do not necessarily reflect the views of my employer.