Software development is a highly social activity.
A developer rarely works on a project on their own, unless they build something small. Even then, chances are it will be worked on by someone else in the next weeks, months or years.
In many cases, the software built runs for a long time, years and sometimes even decades. As such, it is important to consider several aspects, before sitting down and writing any code.
Are you keeping up with new developer technologies? Advance your IT career with our Free Developer magazines covering C#, Patterns, .NET Core, MVC, Azure, Angular, React, and more. Subscribe to the DotNetCurry (DNC) Magazine for FREE and download all previous, current and upcoming editions.
Software Craftsmanship - Abstract
The purpose of this article is to cover software development from the point of someone who does more than just sit in a chair and does exactly what they are told.
It highlights that software development is a highly complex process with lots of smaller steps that are followed in order to build something of quality. Requirement gathering, architecture, testing, writing maintainable code, all come together to create a product that can be supported over its entire life cycle.
The article discusses each of these points and goes into details showing why they are important from an end result point of view.
The article also highlights how our own experience and attitude can have a big effect on the end result.
First, we need to understand the context of what we are building.
There are a number of questions we need to answer before we write any code.
Are we building a brand-new application?
Is it a rewrite?
Is it part of a complex system?
Does it work on its own?
Are there any dependencies?
Do we understand what we need to build?
Do we understand how that piece of software will be used?
Do we understand who will use it?
Do we understand the role of this piece of software?
What about the maintenance of this piece of code?
As you can see, there are a lot of questions to answer straight off the bat. We did not even get to coding anything yet. All these questions give us the context, the conditions and restrictions of what we are building. They basically decide the area inside which we operate.
Why are these questions important? Why do we care if this is a new app or a rewrite?
Well, if this application is a rewrite then we need to understand why we are rewriting it.
Rewriting something is more than likely an expensive exercise and as such, there is usually a good reason for it. Maybe it's built using some old tech which doesn't fit with a much larger system change, maybe we need to add new functionality, maybe we need to make it easier to be used by its users. Maybe we need to make it easier to maintain.
Yes, maintenance is very important.
We might have written the most amazing piece of software the world has ever seen, but if no one understands how it's written, if other developers look at it and run away scared, then chances are that every little fix or change will not only take a long time but will more than likely cause issues.
There is a lot we can do to cover some of these questions and making sure there is a good answer to each one of them is usually a good starting point. As developers, we need to consider that we won't always be there to maintain that piece of software. As such, we can take care of the basics: clean code, good naming policy, SOLID principles, tests to prove it works as it's supposed to work.
If these things are taken care of, then we already are in a good place.
Designing architecture of an application is something we pay attention to, from the beginning. It covers the way the system works and once in place, it's difficult, if not downright impossible, to change.
This is where we start, we get an idea of how everything might work, how things interact, how sub systems talk to each other. It is usually a good idea to do a few proof of concept (POC) little applications to see if everything really works the way we think they should work.
This is also where we might decide that actually, we don't know enough about certain aspects and we either seek help or we learn how to do it.
Knowing when to do each of these things is important. No one knows everything and we should be quite happy to admit what we don’t know.
When we know what we don't know, that's when we learn. That's when we know what we need to learn. As a developer, we have to keep learning, there is so much out there that it can be quite daunting. When that happens, it's usually a good idea to take a step back and go back to basics.
Some people decide to specialize in certain aspects, which is good because they develop very good knowledge on something. Others like to be more generalists so they know some things about a lot of things.
None of these approaches is wrong.
The more you know, the better. More are the choices you will have when making any decisions and the easier it gets to explain why you made those decisions in the first place.
Remember how we said that software development is a very social activity? It is, because you need to interact with others, you need to explain your choices, to articulate why certain things should be done a certain way. It's all about communication, it's all about being open to suggestions and being able to spot a good idea, even if it wasn't yours.
I guess this pushes us into a very obvious direction. Understanding the social aspect in software development and using it to the advantage of whatever you're building. If you remove yourself from the equation and see the bigger picture, then it becomes obvious that the goal is to deliver something good. How you reach certain decisions won't be that important any more. It's the results and outcome of those decisions that matter.
You could of course say that the architecture is not really that important, it will be taken care of by the framework you're using. It could be something like MVC for example.
This isn't what I am talking about.
A framework will give you a starting point, it will help you by providing a few ways for you to build upon. It's usually very focused on a very specific type of activity. This is only a small part of what you are building, so it's good to understand that from the beginning.
However, a framework won't cover dependencies and interactions with other applications. You might even use more than one framework to build something. Maybe you add Entity Framework into the mix, maybe you add others as well. Now you have a number of frameworks in place and you need to get them to play nicely together.
The architecture of a system is decoupled from such things. It helps if you visualize things somehow. Even drawing on a whiteboard will do wonders because it will help you see the gaps.
Be the solution, not the problem.
Software development is a social activity and you will interact with others a lot. It is very easy to let your ego take charge and fight everything. It's very easy to say you're the one making decisions and don't have to explain everything to everyone, but that usually leads to chaos. This kind of attitude won't be good for the project.
People will lose trust, then they start doing other things, if you don't bother keeping them informed (why should they?). Before you know it, the whole thing is going down the drain and eventually fails, because everyone does things their own way without thinking about the good of the project.
This is where the craftsman mindset shines the most. The project comes first. What this means is that you need to consider the bigger picture and code accordingly.
Quick fixes vs proper fixes
Sometimes people apply a quick fix just to get something working quickly. This is usually done to sort out something small that has a negative effect, usually when the system is in production.
What happens on bigger projects is that these quick fixes become norm, add up and given enough time, will make a system pretty much unmaintainable.
As a craftsman, we need to understand these things. We're not just a machine, taking in pizza and coke and churning out code all day and night.
This is a highly logical and creative activity where experience matters, where you can spot the direction where something is going.
Yes, sometimes you might need to apply a quick fix to stop losing money, if a system is losing millions an hour because of an issue, then by all means, fix it as quickly as you can. Once you've done that, make sure a proper fix is in place, don't let these quick fixes add up and kill the system eventually.
What this boils down to is taking the time to understand the issue and put a fix in place which guarantees the problem won't happen again. This is part of a much bigger discussion of course, because we typically have deadlines and only so much time is available. However, being able to articulate and explain the problem, the solution and the dangers of ignoring the problem, is paramount. We're not there just to code, we're there to share our experience and prevent such things from happening.
In a way, attitude is a funny one.
That's because this works better if you are surrounded by people with a similar way of looking at and understanding things. But even if you are not in such a place, then maybe you should show why having this type of attitude works and helps build better software and maybe you can lead the way to change.
The last thing you want is to compromise and accept mediocrity because the product will end up being exactly that, mediocre!
Understanding the requirements and filling in the gaps
Typically, the requirements we get are never complete, they can't be.
Let's take a simple example. One of your requirements is to build a login page with a username and a password. How hard can this be? A page with two fields and a button.
Well, let's see.
The username, is it an email address, a phone number, or something else?
What happens when you press the Login button, but haven't filled in the username?
What if the password is missing as well?
Where do you show the error messages?
What color is used to display the error messages?
Should there be a link to a registration page?
What about a forgotten password link?
What happens if you forgot your username?
How do you get in touch with anyone to recover it?
How do you make sure you don't fall victim to a SQL injection attack?
What happens if a user clicks the Login button 50 times very quickly?
The list of questions gets bigger and bigger. This is only a login page, imagine the number of questions when you build something more complex than that.
This is what we typically do, we look at a piece of work and start asking questions to make sure we build the right thing. We think of anything that could go wrong and we draw on the experiences of the past. Once we put a system live, once we go through a security audit, that helps clarify what kind of questions we should ask when building anything. Experience matters!
Don't get attached to code
This is a funny one. It's very easy to get attached to our code and even be defensive about the choices we make. Other people can't possibly understand the beauty of what we are doing, right?
Well, here's the thing. In the real world, no one is going to care. There is one simple truth out there, the code needs to perform a function, that's it. It either does it or it does not.
By not getting too attached, we allow ourselves to see the flaws and short comings.
It allows us to make changes without feeling sorry for doing it. It leaves the ego out the door and we can then focus on what matters, which is the business value. This is very important in my eyes, the code doesn't exist in a parallel world, it simply needs to perform a function and deliver value, maybe save someone's time, maybe it does more than ever, quicker than ever. There is always a reason why that code exists and if the reason goes away, we can then simply remove it, without any second thoughts.
Focus on the business value first!
We touched on programming being a social activity already. There are many things which matter and being able to support and maintain a codebase, is right at the top. Whether we are part of a big team or even a one-man team, we still need to think what will happen when the code we write goes into production.
Will we be able to look at it in a few months or years and still make sense of it?
Will someone else be able to pick it up and maintain it, fix bugs, maybe even add new features to it?
We all dread legacy code, but if we stop to think a bit about it, we will soon discover that every bit of code we write becomes legacy the moment it goes into production. The shiny new framework we think will solve all our problems, give it six months and it will be old news and something else will be the flavor of the month.
With all this in mind, it makes sense to focus more on how we write code.
So what exactly is clean code? Well, to make sense of some code we need to be able to read it like a story and understand it. This mean that our variables need to have proper names.
For example, instead of
_uRep = whatever
we could use something like:
_userRepository = whatever
If we look at a line of code and it’s not possible to grasp very quickly what it does, then it’s time to change it. Let’s think not only of this moment in time, when everything is clear in our head, let’s think what happens in a few months when all that context is gone and we start from scratch.
So, good variable names and good method names can help with understanding the code and make it easier to maintain it when the time arrives.
Another good approach is to avoid smart and complicated one liners. We all know what they are - those beautiful constructs which do a lot into a single line. Break it up, make it look dumb simple, easy to read and understand.
Hopefully by now, a clear pattern emerges; keeping it simple has never been more important.
Start applying some of the SOLID principles. We don’t need to blindly apply everything, just the things that make sense. For example, the single responsibility principle is a good one to start with. This means we write classes which have one purpose and methods which do one thing.
This allows us to write testable code and this is very important for future maintenance.
I believe it is time to stop justifying why testing the code is important. This is the norm, not the exception. Most of the time we justify not doing things this way because we have deadlines and not enough time to actually write tests. This pushes us into the next section.
If you have a craftsman mindset, then prove it by doing it
We, software developers, need to stop being defensive about what we are doing. We shouldn’t justify why we will spend time to add automated tests around our functionality, we just do it. We can of course, explain what this achieves, but not as a justification, we test things because this is helping the product, because we are craftsmen and because we know what we are doing. When someone asks us to estimate a piece of work, we will do it, with testing in mind.
Testing is part of our work, not a nice to have.
No one tells a plumber how to do his work, they quote you for a job, factoring in everything their experiences teach them and so do we. So, let’s stop justifying what we know needs to be done and let’s just do it. The product will be better off because of it.
This takes us back to the cutting corners side of our industry. As I said, let’s be part of the solution not the problem. We know that cutting corners doesn’t help, we know that continually introducing technical debt will eventually introduce a huge cost, so it might be better to do whatever we can to minimize it.
Here’s a quick test for the current project you work on. Have a look and see how many technologies are used, how many frameworks, how many libraries.
Some nasty surprises might come out of this.
How many of those are there because someone wanted to use a particular thing?
How many libraries are there because someone needed one thing to be done, in one place and instead of writing a small method they introduced a whole new library to do the job?
This can all be solved if we switch the focus from ourselves, to the project itself. Only add something new if there is a clear, long lasting benefit, if it can be honestly justified through something more than just personal desire.
This is the big elephant in the room. People outside this profession will see that and immediately think, how much longer this will add to the development process. Did you just say 30%? No, we haven’t got time for that, we’ll test at the end!
How many times have we heard this? Of course, we already know the benefits of writing testable code and then adding tests to actually prove that it works as expected.
Imagine having these unit tests which run very quickly and thus having the certainty that the system behaves in a predictable way. Imagine being able to refactor without the fear that you might break something. Imagine getting a bug report, writing a failing test which highlights the bug, then fixing the code to make the test pass. Imagine having one more test which guarantees that the particular bug you just fixed won’t show its ugly head again.
These are all great benefits already but wait, there is more!
Imagine not having to keep testing the whole system every time something changes, imagine the maintenance phase being much shorter and less expensive because you don’t need so many manual tests anymore. We need to be ready to articulate why this way of working is better, even among us, even to other developers who may not code the same way. This helps bring everyone up to a higher level, work in a modern way, enjoy the work process for a change instead of running around doing countless extra hours, whenever a launch is looming.
Hacking your own code or security is not something you think about at the end
We are now moving into that area that no one likes to talk about, which is security. It seems to be something we always think about at the end, for some reason.
How do we build secure code? This is not so trivial, but it requires developers to think for themselves, because most of the time this area will not be covered by requirements. So, we are the ones who need to make sure that the beautiful login page we just wrote is actually secure.
We are the ones who see a URL like this: www.mysite/user-details/1 and think to ourselves, “hmm I wonder what happens if I replace 1 with 2 in the URL and hit enter”? We are the ones making sure that sensitive data is not exposed. We are the ones making sure that we build an API that can be accessed only by the intended audience and no one else.
This is a huge subject which deserves an article of its own. For now, because we have the attitude of a crafter, we can look at our login page and start thinking, how would we hack it? Maybe we go and read a bit about ethical hacking, get some ideas, see how the other side of the world functions and try to hack our own code. It won’t be perfect, it won’t be 100%, but it’s a start.
Focus on something for a change
We all want to work on the latest and greatest, but that’s not always possible.
Whether it is the latest framework, the latest version of something, maybe a new database system, maybe a new API type, whatever it is, we want it! That’s not necessarily a bad thing, it’s good to stay up to date with latest developments. It is however, also good to realize there is a balance that needs to be maintained.
By jumping very quickly from one thing to another we risk not learning anything deep enough and we can end up with surface only knowledge. That’s definitely not a good thing!
There are a lot of products written in languages and versions which have not been current for several years. Someone still needs to maintain and maybe even upgrade them. Businesses will rarely accept a complete rewrite of an application which does the job just fine. That’s to be expected and we need to understand that. Yes, there is always something we can improve even in older products and if we can provide enough of a reason for them then certain changes can and will happen.
It’s all down to us explaining the value of a change to the clients. It won’t be done in a technical language and it will have to be understood by non-technical people. It’s all down to communication skill and business value.
As you can see, we touched lightly on a lot of subjects. None of these is truly ground-breaking or amazingly new. It’s easy to forget certain aspects though. I hope this article shows you how big a software project is, how many moving parts there are and how important they all are. The most important aspect is our knowledge. We are the ones with experience, who can see the defects a mile away, who can predict if something will work or not, based on hard data and our own tests.
We need to keep learning, keep educating ourselves and keep educating others. If we are in a team of ten developers and we help them improve 10% each, that’s a total of 100% for everyone.
Our own value is not the only one we bring to the table, what we can do for others matters sometimes even more.
We need to have more faith in our skills and take charge, make sure the right things are done, make sure there still is a balance and we can help build a product that can sustain the test of time.