How to make better decisions
As software developers, we take small decisions all day along. They range from code level (which variable name to choose) all the way to architectural decisions (use a document database or not?). Together, these decisions turn out to be very important; they can cause the project to succeed or fail. How can you make better decisions? Read on!
A thought experiment
Since is a new blog, let’s also imagine you just start with a new software project. On day one of the project, you start with the very first feature. This means: setting up a basic project structure, getting the inputs ready, do some calculations, and presenting the output. Within a few hours or perhaps a day, you have the first feature ready.
Then you’re ready to start working on the next feature. As the first feature already involved getting the project setup, this second feature will require less source code. The third feature might take even less than the second, and so on. When you get on with the project, something remarkable happens: on average, new features require less source code to write. But yet it takes more and more time to add a feature. What’s more, users start reporting bugs that need to get fixed, which also requires time. The more features you add, the more time each feature will cost.
Getting out of the vicious circle
Is there a way to get out of this vicious circle? Is it possible to write software in such a way that it stays easy to add functionality, and at the same time requires little maintenance?
Ever since I experienced this phenomenon, I have been thinking and experimenting with possible solutions. Sharing all my experiences in one blog post is not possible, so I’ll give just a high level overview of what I have learned.
How can we write software in such a way that it stays easy to add functionality, and at the same time requires little maintenance?
Lower the defect rate
If you want to get quality code (that is: code that has few defects and can quickly be changed), then you need to commit to only deliver software if it’s quality is good enough. Only mark your feature done when you’re sure it does what it is supposed to do. Essentially, this means that you do work to find the flaws beforehand, before the user reports them. So it may cost you some extra time beforehand, but it will save you more time later. (Keep in mind that fixing a bug afterwards is always more expensive.) There are lots of tools and techniques to prevent issues from making it to production, use them to your advantage!
When issues are found in production and they are severe enough, all kinds of unfortunate things happen. You’re forced to drop what you were doing, only to fix the bug as fast as possible. You can’t continue with your current work, so you’ll be getting behind schedule. Customers are getting less happy with you, and you may feel forced to fix issues by introducing all kinds of hacks or shortcuts. But later, those same shortcuts work against you when you want to add new functionality, making you run behind schedule even more. Therefore, bugs in production can really hamper the speed of your current development, even when they were fixed long ago. This make it important to do whatever you can to prevent these bugs, and deliver only once you’re sure enough that your software works.
I know that, if you’re working in an environment where deadlines are tight, this is not easy to do. One important advice: whenever you fix a bug, never just stop there. Always take a step, however small, to prevent similar bugs in the future. This can be a small refactoring, adding a unit test, or better documenting things. There is always an improvement that pays itself back within a short time.
Readability is just as important as correctness
Would you actually write big software projects in Assembler? I certainly wouldn’t if I can avoid it. Why not? Because it is very difficult to read, which makes it very difficult to find errors; even a function call is multiple lines of code!
This shows the importance of good readability. Nowadays, we are using high level languages, but it is still easy to make things unreadable. What decreases readability? In short, everything that obscures the intent of the code. So, make sure you use the right naming, have concise functions that do what they say, and avoid antipatterns.
Decide to deliver
How would delivering early and often improve your code? That is because the production stage of the software gets you the most valuable information. It is the only way you can tell that the user likes your software, or that he uses the software like you thought. It is the only way that you can tell whether you solved a problem for the user. This is an immensely useful form of feedback, so you should do what you can to get this feedback as early as possible. Therefore: deliver early and often, and get to know what your user wants.
This feedback also has other effects: when you deliver a feature early, the user gets its value much earlier. This deepens your understanding of the user’s domain. As you learn more and more, you get to see opportunities to provide added value with little effort, and you learn to understand which new functionality is required first. Also, when you deliver often, you can only deliver small parts of functionality. This drastically lowers the chances that you introduce blocking issues for the users.
This advice is actually a special case of a more general insight: deliver as much value to the business as you can. That’s a mindset different from the only way many developers think: they tend to think about how to implement the feature, not about what users might want and which solution direction delivers the most business value.
Keep it simple
Let’s get back to the assembler example, because there’s still another reason why assembler is difficult to understand: there is a lot of complexity you need to know before you will understand what is going on. For instance, you need to understand the processor architecture, which registers contain which data, which values are on the stack, and so on. Stack and register values change you call functions, so they may contain unexpected values. Assembler opcodes are generally very simple, but they are very low level. This causes the intention of the program to be buried into tiny details. This makes even a simple function look complex.
Simple, well defined solutions are always favored over complex ones. In practice, even moderately complex solutions usually turn out to have lots of special cases, which can only be solved by adding more logic, after which it often turns out that even more special cases show up. This makes the software more and more complex, and this complexity makes the software difficult to maintain and adapt. Therefore, if you feel that the software is getting more and more complex, rethink the design choices that were made. It is well possible that it turns out that some choices weren’t the most practical, or just won’t hold anymore. If so, work towards the choices that result in a simpler software solution. If you stick to your initial ideas, complexity will keep increasing and will slow you down more and more.
Keep learning
The world is changing. Therefore, your business is changing. And the software development landscape is changing even faster. In order to stay valuable, you need to keep up with these changes. Moreover, in order to add the most value, you can increase your personal skills; not just development skills, but also soft skills. There are always opportunities to learn, and the Internet provides some great sources of information about this. Keep learning to make better decisions!
Decide consciously
Each of these tips is really just scratching the surface, there is so much more to tell about this. If you want to learn more, be on the lookout to improve your insights as much as you can. Be conscious in what you decide and don’t go for just the first idea that pops up. Find ways to get feedback as much as you can. This will eventually lead you to making better decisions. Because the choice is yours!