10x Engineer: My 4 principles of software engineering
Sunday Mar 7, 2021 | Series 10x EngineerThese are 4 principles I try to adhere to when designing and developing software systems. They’ve worked for me at different levels of abstraction and have enabled me to rapidly deploy even complex systems.
Lets have a look at what those are and give them some context on why I think they’re important to keep in mind.
#1 Agility
Agility is not just a way of working. Your codebase, deployments, decision making and leadership needs to be agile too. Afterall you can only be as agile as your least agile link. So lets break it down a bit and see what tools we have to stay agile.
… for codebase
This is probably the most talked about part among engineers, especially when you’re starting out. We’ve probably all heard about SOLID principles, so I’ll just pick out the 2 that I think will give you the most bang for your buck.
Single Responsibility will really help to keep your code testable and keep requirement changes from requiring you to rebuild the entire code base. It can be tricky to find out where to draw the line, my tip would be to use testability and the amount of dependencies as a measure for when to start splitting things off.
Dependency Inversion Principle to keeping your layers loosly coupled will help inmensely when making future changes or extensions. It also allows you to more easily swap out implementations, both in testing and in production.
Then there are a couple of other tools that I like to keep in my belt to improve the codebase.
Test Driven Development will help you a lot to stick to the above two principles. Make sure to revisit the Gang of Four design patterns regularly to see if those can help you keep things more maintainable. Use it as a tool and not a dogma though, sticking to it without weighing the benefits can actually cause you to get less agile.
Boyscout principle i.e. don’t just do the minimum implementation and make sure you cleanup and refactor a little bit at a time for better reuse later. In other words try to integrate new features and not just add them on top. You will end up with a far easier to maintain application, with added bonus that you’ll start to accelerate with new features too. Admittedly this is one of those things that I tend to forget when I go focus mode and try and deliver features asap, but it is important to not later have to do tech debt stories in a dedicated sprint.
… for deployments
Aside from having your deployments automated there are a few things that you can take into account to accelerate your releases.
Fault Tolerance - I’ve recently done a post on fault tolerance and it basically boils down to having a general safety net that will allow you to focus on forward fixing. Instead of having to do damage control, hotfixes or rollbacks in the event of having made a mistake. We’re still human so we will make a mistake it doesn’t mean you get to get careless, however it does take the pressure of.
(System) Testability will increase your confidence when deploying and will make it less of a hassle to deploy multiple times a day. It allows you to automate a lot of the testing and leave just a handful of checks to be done manually if any.
… when making decisions
Postpone choices - I have found that at times that I have trouble choosing what direction to go with, that trying to critically think whether I need a decision right now necessarily. Maybe we can postpone the choice until a point in time where we have more information or maybe alter the implementation slightly with minimal effort so we no longer need to worry (for now) what direction to head towards.
Lean - keeping in mind that removing things is also an option often gives you more room and can safe you headache or maintenance down the line. We often are very focussed on adding new features but that also adds complexity, needs maintenance and testing. Instead we can also look at removing things we don’t care about (anymore). Even when bugfixing this might be an option, e.g. can we fix this issue by removing an if
statement instead of adding another exception.
Things that are not there can also not form adependency with other systems, libraries and/or other teams. Making you able to move faster because its one less component to manage.
Keep options open both of these principles do come down to keeping your options open. Which I suppose is why we want to be agile.
#2 Deal with uncertainty
The world tends to be uncertain and predicting the future is difficult. In that vain I try to stay away from making predictions. However any choice you make will limit what you can do in some way. SQL vs NoSQL, Java vs Python… the tools you choose define what solutions you can implement for example. So instead I like to think about what options I’m about to eliminate to make a choice.
As stated before, if you can move forward while eliminiting the least amount of options that is probably the way you want to go. Of course it still needs to align with where you want to end up. So Keep options open…
This also requires you to embrace the grey area. The more complex the problem the less overview you’ll have, even more so when you have no implementation yet and are still in the design phase. It is paramount to start with something so you can test everything you think you understand. Fail fast for things you’re not sure about and implement and lockdown the things you do know. While making sure to have a safety net to recover from failures.
Don’t hold on to the rules too closely, play with them and embrace some uncertainty and dare to make a choice while adhering to keeping your options open
#3 Transferable patterns
Sometimes a wider understanding of some of the patterns will mean that they’re applicable to more situations or in different levels of abstraction. However to get there you have to really understand all the ins and outs of something to then be able to apply it in other situations.
The same goes for transferrable skills, they can really give you a head start when starting in another domain or problem.
#4 Deploy with confidence
Instead of having a target for test coverage I like to track my confidence in deployments. Not based on past deployments, but on how certain I am that this change will be correct, stable and easy to fix if does turn out to be faulty.
This confidence would take resiliency, fault tolerance and correctness in isolation into account.
If I don’t feel confident I can then look at what tool I want to use to fix that. That might be more system tests, easier integration tests or maybe as drastic as refactoring something to make it easier to fix or support.
In general the hard skills are often very easy to identify and attain. These soft(er) skills are harder to get but pay of in so many ways if you do get them.