In the world of software engineering, there are many different protocols, practices & procedures, it can get a little confusing, but generally, in my personal belief, there are some that are more crucial to ensure that the software is able to ship with success. In addition to ensuring that the software is clean, maintainable, scalable, etc. Let me start by stating how a lot of the stated practices/principles will overlap to some extent or another, but it just proves a point that one should at least take them on board.
Simplicity in itself is beautiful, whether it’s regarding interior design or food, you can’t go too far wrong with simplicity. In the world of software engineering, even regarding complex tasks, there is always a way in which we can implement a simple & clean solution. One thing that I hate is having to deal with code-bases that have many over–engineered implementations, unfortunately, I’ve recently had to deal with a first class example. Recently I’ve dealt with some code where the code/logic itself was a single line of code (regex), but the comment associated to this code was over 100 lines long.
It’s a personal belief of mine that you should always include block comments, but you should never have to include comments to that degree. If you feel the need to include that much detail into explaining what the code does, you should probably refactor the code to make it more readable, more maintainable, in turn improving the quality metrics. Due to how often simplicity is emphasised, there are many different principles out there that just boil down to simplicity such as KISS.
While all developers should at least know of the SOLID principles, it’s insane how many don’t actually put them into practice. Whether it’s just single responsibility or ensuring that there’s a lack of dependency inversion, one prime example being how I’ve been tasked with refactoring an application to clean it up, simplify it, etc. It currently uses Apache camel, there’s nothing wrong with that, however due to the high level of dependency of camel, it’s turning out to be an immense nightmare trying to remove camel from the code base. To add a little more background to the application, we decided to remove camel since in this precise use case, it’s an over-engineered implementation for routing & essentially a chain of responsibility design pattern to link the processors together, apart from that, it serves no additional purpose in our case.
I find that following the solid principles will ultimately ensure that the code base is simplistic to some extent or another. Without going off on a rant. I cannot emphasise enough how many times I’ve seen a developer try to implement an infinite amount of functionality into a single controller/service/utility class, if I had a penny for every time, I’d eventually be a very wealthy man.
To summarise what SOLID actually means, here’s the formal definitions:
- Single responsibility
- Liskov Substitution
- Interface Segregation
- Dependency Inversion
Now, I will make the assumption that most of you will know what each principle means, but just to add any clarification, single responsibility; this just means that a class or some implementation should only do one thing & do it incredibly well. Open closed; this just means that your software should not alter existing behaviour, but rather add to it, almost a functional way of thinking where you never alter existing state. Liskov substitution; this is more specific to OOP, which may not necessarily be the paradigm that you’re using, but for arguments sake, let’s say that you are. The substitution principle essentially means that sub-classes must be able to have a set of functions without knowing. A great example of where you may violate such principle can be found in this post, which if you’re unfamiliar with this principle, I highly suggest you read.
Interface segregation; this essentially means that some class or implementation should not be forced to rely on some method it doesn’t use or need, simple enough really, common sense should support that in my opinion. Finally, dependency inversion; this just means that a high level implementation, class or interface, whatever should not rely on low level implementations or modules. Rather the preferred solution is to have some middle layer to act as a layer of abstraction, which is always a nice feature. An example being how you may have some Java project, for whatever reason you want to stop using Hibernate, provided you have the right level of abstraction, this could or at least should be a small amount of code to refactor.
The least principles are probably less known than the SOLID principles, but again, these principles should be common sense really. One suggests that someone with a reasonable amount of knowledge around the relevant subjects should not be surprised when looking at or interacting with the relevant code base for the first time. The other states that the code base should require the path of least resistance to understand, maintain, etc.
Again, this should be common sense & to an extent this principle or set of principles can also be associated with risk & whatnot. In a nutshell, the principle of economics states that a developer should always chose the option with the most or largest set of benefits with the lowest impact or risk. This may be more critical depending on what sector you work in, since I work in the world of finance, this is obviously quite an important principle to myself.
Like anything, the higher the level of quality the better, but with software development there are many metrics of quality from the common ones like unit test coverage & computational complexity through to quantitative metrics such as Cyclomatic complexity & Halstead complexity.
Another measurement of quality, in my personal belief is how many times an application throws some error or an uncaught exception. This can be measured through monitoring tools, obviously this specific topic requires more context, since the application may be designed to handle very error prone processes, etc.
This is again, another practice that is VERY OFTEN overlooked, I can’t complain enough about how little documentation exists in the current ecosystem of technology. But for me, the basic level of documentation that should be provided should at least cover the following questions:
- Why does this feature exist?
- What are the project requirements?
- How does it work?
- Does it require any configuration?
- Does it communicate with any other features?
- If so, what other features?
- If so, how will it interact with other features?
- Who are the stakeholders?
A lot of the time, teams will not have a technical writer or a business analyst to cover an array of the documentation requirements, including project requirements. So that basically means that the developer needs to pick this task up, in smaller teams, often developers have to wear many hats, from business analyst through to technical writer, to project manager, the list is borderline endless in some cases. In addition to this, working in sectors where risk & data are critical, such as the financial sector, you may even want to consider versioning your documentation for the likes of auditing purposes.
As a basic conclusion, I typically try to ensure that my code base is so readable that you’re able to make sense of the application flow at a glance, without having to drill down into the deepest implementations of the code base. If a junior can make sense of it, generally, I think that’s another good indicator, obviously that may not be a fair metric since some junior developers are just pure bloods, naturals. But I’m sure you get the gist, if someone with a lesser amount of knowledge on the application & application flow is able to pick apart the application with a large amount of ease, then generally speaking, you’ve done a decent job. The only time I’ll argue that you’ve done an amazing job is when you’ve achieved your desired code coverage, whether it’s via unit testing alone or through the inclusion of integration testing. In addition to ensuring that the application handles all possible errors or exceptions, whether they’re anticipated/expected or not, in addition to ensuring that you’ve achieved the best quality metrics you possibly can.
If you do a good job of all of the above, I can assure you that you’ll succeed as a software engineer, in addition to making management & governance happy! 😁 Furthermore, you’ll ensure that you’ve planned ahead, prototype with some ideas, etc. While this may not be ideal from a business perspective, since you’re not spending all of your time writing code, this can be deemed a loss. However, the end product will be of a much higher quality, meaning that in turn, the end result for the business is much better! 🙌 – This can also reduce cost for the business in future, since it’ll take less time to add new features or alter existing features, in addition to removing possible bugs. Although if you have done an amazing job, you could argue that the application wouldn’t have any bugs in the first place.
While this last tip may be off topic to the main body of this post, you should always be learning, whether it’s just learning new practices, languages, paradigms, etc. If it can make you a better developer, even if there’s just a chance that something can make you a better developer, DO IT. Strive to smash the standards, strive to exceed expectations & generally, try to think of your colleagues, make life easier for them! 😉