Near the end of my first year in my first development job at Andersen Consulting (now Accenture) I was handed an exciting new project: to develop a Monitoring And Reporting Server for a major bank’s financial systems. And to do it in a few weeks.
This was the mid-1990’s – in the very early days of the web and long before the ELK stack and other tools that would have made this pretty straightforward. In addition, we were developing this with C/C++ and had to write it all from scratch as open source was still in its infancy and Java and C# were still to come.
But what software engineer doesn’t like these kinds of challenges? I was feeling pretty good about myself – I’d had a couple of solid wins up to this point, so in some ways this project was a nod to my success so far and a challenge to step up from pure coding to design and architecture.
Under the tutelage of my supervisor at the time I sank my teeth into it. Given the challenges – no open source, unforgiving programming environments, limited compute and memory, limited time, etc. – you’d think I’d try to keep it simple. Right. On paper (yes, we still used paper) I created an incredibly sophisticated design with input channels (“Translators” in design pattern parlance, but even design patterns were in their infancy), normalization layers, storage mechanisms, and “bots” to look for anomalies. It had self monitoring and auditing and adapters for new pluggable streams. For good measure I designed it all to interoperate with other systems and technologies using a (complex and slow) CORBA ORB. I abstracted everything just in case some future unknown requirement might require extensions or adaptions. I was very proud of it.
It was never completed.
Thank goodness. While I was disappointed at the time, I realize now that this creation was designed to be a total monstrosity and likely a failure. Soon into the coding I was already bogged down with massive issues in keeping track of the (hand written) thread pools as well as challenges managing the complexity of the modular system builds. Had it been completed it would have taken a team of rocket scientists to understand and maintain what was, in the end, a pretty simple concept. Fortunately another project came along with higher priority and I was put on that. (Plus, though it was never said, I think my supervisor knew deep down that my approach was leading us to a bad place.)
What went wrong? Easy – I’d violated the first rule of software architecture: SIMPLIFY SIMPLIFY SIMPLIFY.
Duh! Right? Anyone in the field for more than a few seasons knows that more complexity leads to higher risk, more bugs, longer development times and greater total cost of ownership (TCO). But we do it anyway. The wisdom gets lost in the excitement of creating something big and beautiful.
It happens all the time. Even with all of the “bumpers” to keep us honest these days – solid design patterns built into many of the open source frameworks, Lean software practices that naturally select for simplicity – I still see it violated on a regular basis. I spent several years consulting for large corporations, largely fixing violations to this rule. Even today some of the biggest challenges my team faces at Bonial come from systems and projects that were simply too complex for the work they did.
So, how do you implement this principle in practice? There’s no magic – you just challenge yourself or, better yet, have the team challenge you. When you’ve come up with your approach or design, ask yourself, “Ok, now how can I make this 30% simpler?” Look for over-design especially abstractions and facades that aren’t really needed or could be added later. Challenge the need for all concurrency implementations especially when app servers are in play. Look hard at the sheer number of moving pieces and ask whether one object / module / server will suffice? And always look for software, best practices and frameworks that already do what you’re trying to do – the simplest thing is not having to do it at all.