The Art of Simplicity
Introduction
In the past, I’ve shared a few thoughts & ideas on what I personally consider to be clean code. Since then, I may have changed some opinions & views on certain topics, but one element I still firmly stand by. That is simplicity.
An Understanding
It’s hardly an unheard of situation where a set of developers, be that a team or an individual are faced with a complex problem. It might be that the problem by default isn’t complex due to a technical nature, but due to factors external to technology, such as laws, policies, performance constraints & so on. There are many, many factors that can dictate the complexity of an application.
But it’s amazing to see that to this day, regardless of the tools being use, regardless of the shiny new frameworks that are being used, etc. Software engineers have a really bad habit of over complicating things sometimes. I’ve seen code where there’s just layers upon layers of abstraction, to the point where it’s fundamentally confusing to know which layer of abstraction you should be working on.
I personally like to follow a relatively simple philosophy. If ever you find your code is hard to read for a less experienced software engineer, or even a nontechnical person, then you’ve probably done a pretty bad job on the design. I am a firm believer that at a high level, your code should be a lot more declarative in nature. Yes, as you dig down into the implementations, naturally, things can get a little more hands on with business logic, error handling & so on.
But you I do believe you should always strive to write it in such a way where it’s almost a no brainer to make sense of. Even in the lower levels of the codebase, I think you should try your best to simplify things as much as you reasonably can.
It’s also reasonable to assume that there are many different factors that essentially over rule focusing on a well designed system, such as time constraints. It’s understandable that a start up might need to get an MVP out of the door ASAP to generate more revenue for the business. It might be that a large scale enterprise might need to rush some implementation to be inline with a new legal policy. There are many external factors that will apply pressure to developers to rush.
Ethics
In recent years, I have heard more stories about scenarios that have resulted in real world & sometimes legal consequences regarding the creation of some botched software. A fine example being the ongoing/recent Post Office scandal in relation to Fujitsu. I’m not targeting those companies directly, but I’m using this big story as an example. If anything, at one point I was considering in taking a job at Fujitsu, I had an offer & everything.
But getting back to my point, I for one firmly believe that software engineers should be held to a code of ethics. I do believe that if you’re creating bad software & you’re aware that it’s bad, then yes, you should face some consequences. Granted, it’s not fair to say that this should be the case when it’s simply an accident, or a scenario occurs where a brand new vulnerability might be exposed, etc. There are situations that I agree should allows software engineers a bit of breathing room, I don’t think all issues tied to some software application should land on the shoulders of the software engineers.
But when there’s a bad result, which has arisen from poor design, rushed decision making, a lack of quality control, testing & so on, then yes, I do think there should be some consequence for such a scenario.
Circle Back
To get back to the point of this article, I believe that you can ironically design a better system while aiming for simplicity, develop something that works with what you know now & the requirements of today. Time & time again, I’ve seen developers & teams fail to meet a deadline for some project because they’re trying to future proof it or make it scale to the moon & back again. Not every software application needs to scale like Google’s search engine, and ironically, if that ever is a problem, usually that’s a good problem to have anyway.
Ironically, to support my theory of simplicity being a good thing. Not only is it good for the developer(s), but I’ve found in the past that it has a direct, tangible benefit to the performance also, an example might be mapping objects & so on. If you’re simply mapping objects for the sake of it, let me ask you one thing, why? – From a performance perspective now, you’re not only creating a new object, which is basically the same as the original object, just named differently, but now you have twice as many objects for the garbage collector to work with. Granted, I understand that it’s not always so simple to stand by this argument, but I have seen many cases where a developer has mapped 2 identical objects for no tangible benefit, other than they’re simply doing it for the sake of it.
I appreciate that not everyone might agree with that perspective, i.e. mapping a value object to a data transfer object, I can appreciate & understand some of those perspectives. But I also stand by the policy that it should be done on a requirement basis, as & when needed.
Conclusion
I would like to start my conclusion by stating that this article is 90% opinion & I can totally understand the perspective of people that may disagree with what I’ve said. That doesn’t mean they’re wrong & it doesn’t mean I’m wrong either, in summary, I think you should apply the agile concept that you want to maximise the amount of work not done. In my humble opinion that should include cognitive workloads.
I appreciate that some of what I’ve said may even disagree with design patterns such as DDD, as that can often include mapping a lot of objects for many different reasons. I don’t disagree with DDD, if anything I quite like it, but DDD. But I also believe that DDD is the kinda direction you might want to take when dealing with a lot of complexity, in which case, mapping a lot of objects all the time might actually make the over all solution a lot more simplistic. So I don’t by default disagree with the any of ideas around DDD or anything like that.
I think context also plays a big factor, because you might be working with one team or you might be working on one project where simplicity is fundamentally different to another. I think it’s a matter of using some initiative & clear communication to establish what simplicity looks like for your team & the problems that you’re dealing with.
But I still think that it should be stated that you should only introduce complexity as & when it’s needed. Much like the fundamental idea of microservices, I’ve known small teams in startups to just jump blindly into writing microservices, when that’s not by default the best direction to go in because microservices have a lot of challenges themselves. A very simple example being the utilisation of proper design patterns, such as a circuit breaker & implementing the likes of retries with exponential backoff. And what happens once you’ve hit the limit on the number of retry attempts? – Out of the box, there is no right answer. Just like there’s no right answer on what simplicity looks like, but I will stand by the rule that simplicity should be something that you strive for.