At the start of this book, I said I wanted to teach you how to be a great software engineer - not just a computer programmer. In this chapter we’re going to take a closer look at what that really means.
By now you’ve completed 9 C# code Katas. You’ve looked at all sorts of technical concepts, from Standard Streams to objects. You’ve seen how these work in the C# programming language. Not only that, but you’ve put these skills into practice by completing the ‘Try on your own…’ section of each Kata.
Up until this point, the Katas have given you more or less step-by-step instructions on how to complete the tasks. However, you’re now reaching the point where you have enough skills now that I don’t need to tell you every single line of code to write. This chapter is going to provide you with a final push to solving problems using your own initiative. It’s going to equip you to understand how to approach a programming problem that doesn’t have an obvious solution. To do that, you need to start thinking like a software engineer - not just a computer programmer.
The terms ‘software engineer’ and ‘computer programmer’ are used pretty much interchangeably in most things you read. However, there is an important distinction between these two. Simply speaking, a software engineer is someone who understands the entire discipline of creating software to solve a real-world problem. They write code for sure - but also spend a lot of time understanding how their program fits into the real world, how to make their software easy to change in the future, and how to evaluate their software’s performance.
On the other hand, a computer programmer is just someone who happens to writes code. For instance, someone who writes macros in Microsoft Excel is, technically speaking, a computer programmer. However, it would be a stretch indeed to describe them as a software engineer! Therefore we can say that all software engineers are computer programmers, but not all computer programmers are software engineers.
Why care about the distinction? Well, if you care about creating great software, you want to be a software engineer! You to be someone who can write reliable software that meets the needs of it’s users - not someone who hacks away at marcos in Excel on their lunch break to make their job a bit easier!
Every computer program ever written exists because someone, somewhere, had a need for something. That need for something can be as big as your imagination - ‘I need a way to send messages to my co-worker in Spain’, ‘I need a way to launch ballistic missiles half way across a continent’, ‘I need to be able to produce text documents without any spelling mistakes’. Software engineers try to meet these needs by creating computer based solutions - primarily through computer software.
Software engineers consider needs, such as the above examples, as necessary ‘requirements’ for a potential software solution. The first step in writing good software is to understand the requirements of the software. Now this is actually way more difficult than it sounds. Software companies dedicate huge amounts of resources to researching how their software can better meet the needs of their customers - and they still get it wrong very often. In fact, defining software requirements accurately is one of the biggest tasks for software engineers.
Why is this so? Well, simply put, in order to understand the requirements for a given piece of software, you need to work with humans. Humans, as it turns out, don’t communicate with clear, unambiguous language. Instead, they’re emotional, they’re irrational, and they sometimes don’t even understand their own needs fully! I’m sure you can identify with this, as a human yourself!
Let’s look at an example of how defining requirements for a software solution can be difficult, and how a software engineer can overcome such difficulties.
Let’s say Bob is the owner of a fast food restaurant. Bob wants a computer system that can tell customers when their order is complete. He hires Alice, a software engineer, and explains this requirement to him. After a brief discussion, Alice tells Bob she understands the problem. She goes back to her office and spends a month creating a software system for Bob.
When the software system is finished, she brings her laptop in to Bob’s restaurant. She demonstrates her work to Bob - and - disaster - Bob hates it!
‘Why?’ said Alice ‘It does what you want!’
‘No it doesn’t’ said Bob.
‘What’s wrong with it then?’ Asked Alice
‘Well for starters, it doesn’t text the customer’s phone when the order is finished. What if they leave the shop whilst the order is being prepared?’
‘But you didn’t tell me it needed to do that!’ protested Alice ‘How was I supposed to know?’
‘Well you know now’ Said Bob.
Muttering under her breath, Alice goes away and spends a couple of days adding the text-when-ready feature. She returns to Bob and demonstrates the software again.
‘It’s still not right!’ Said Bob ‘What if there’s a delay - how do I increase the time estimate once the order has gone through?’
Feeling a vein begin to pop in her head, Alice storms out and begins angrily typing at her computer. When she’s finished, she demonstrates the system to Bob again.
‘It’s still wrong! What if someone is sick on the floor? How do I send an alert to the cleaning staff?’
Hopefully you get the picture! Alice and Bob started out with the best of intentions, but weren’t able to get a clear understanding about the requirements of the system that needed to be built. Bob assumed Alice could just ‘wave a magic wand’ and create the exact sort of software he needed.
Bob’s last compliant indicated that he wanted the project to be way bigger than the original description of ‘tell customers when their order is ready’. Bob and Alice misunderstood each other, and as a result things got a bit emotional during their meetings. The whole process turned into a bit of a shambles! Note how this mess has nothing directly to do with writing code - it’s all about human interactions.
So how can this be improved? Well, first, an general description of the system needs to be formulated. Next, Alice and Bob need to come up with a list of precise requirements that the software needs to meet. These requirements need to be agreed by both Bob and Alice. The project also needs to be scoped appropriately - it needs to address a single problem. This information can be compiled into a document called a ‘requirements specification’.
A sample requirements specification for the software might be:
Order information system
If Alice and Bob had agreed on something like the above, then the process of designing the software would be a lot smoother!
Once a computer program is ‘finished’, how do you make sure it really works? That is actually a much more difficult question than it seems! Broadly speaking, answering the question Does it work? involves answering two sub-questions: Does the code do what it ought to do?, and Does code do what the user wants it to do? These are actually very distinct questions! To answer them, we need to undertake software testing.
This question is about finding problems and incorrect behavior in the code itself. The simplest way to go about this to run the software with a particular input, and check if the output is what you expect. This is known as a ‘test case’. You can create a set of test cases, and systematically run the program with them to see if it does what you expect. If the program’s output differs to the expected output in the test case, then there is something wrong with the program - a ‘bug’. Test cases can be grouped together under a certain description or header that explains the part of the program they’re testing. Together this can be termed a ‘test specification’.
Alice’s test specification for the Order Information System looks like this:
Description: It displays the correct name when a meal is ordered
Alice then runs application with inputs specified by the test cases, and checks the display to see if it is correct. Here’s a sample of the test run:
It turns out there are bugs in the system! Although the first test case was a success, the second and third ones failed.
By inspecting the output under ‘Actual meal name on display’, Alice notices that the system only outputs the first letter of each meal name with a capital letter. For instance,
Chicken Chow Mein becomes
Chicken chow mein.
The letter capitalization might seem minor, but it’s important for Alice to have a record that the software behaves like this. Alice can then decide later if that’s something she wants to fix.
The more pressing bug is that the software isn’t able able to display numbers at all. Notice the missing
8oz Beef Burger. This is a serious bug that needs to be fixed.
With systematic testing processes like these, software engineers can check that their software is operating correctly. To make things easier, Software engineers will often write automated tests which can run the software with test cases automatically. However, how exactly one does this sadly beyond the scope of this chapter.
This is the second aspect of software testing, and perhaps the most important part. It’s no good creating a program if no one finds it useful! So how do we know if the system really meets the user’s needs?
The simplest way is to get the system running in a real-world scenario. Once the program is ‘in the wild’, it’s possible to check it’s performance against the requirements specification to see if the software really does what it was intended to do.
For instance, the first requirement for Alice and Bob’s computer program was Display a list of in-progress orders. After having used it, if Bob can agree that the computer program really does Display a list of in-progress orders, then Alice can consider the software to truly meet that requirement.
Another part of this process is to ensure the users are happy with the product. It’s possible that a piece of software meets every requirement on the requirement specification but still doesn’t make the users happy! Interviewing the users after a trial run, for instance, might be a way to see if the program is meeting their needs.
It’s inevitable that Alice didn’t get it right the first time. There’s no way she can understand all the specific needs of Bob’s business after a single attempt at writing the software. More than that, it’s difficult for Bob to understand how the new software system can fit into his business without a trial run. That’s why it’s important to keep going away and improving the software based on feedback from the end users. If Alice and Bob can co-operate in this then eventually Bob will get the software he wants, and Alice will get the satisfaction of knowing she wrote a great program! (…and a paycheck!).
Every software project you ever work on will involve things you don’t understand or know how to do. It’s inevitable - a fact of life. Being a good software engineer is knowing how to be resourceful in solving problems where you don’t have all the answers.
The most basic skill for this is knowing where to look when you’re stuck with a programming problem. This usually means Googling for the answer to a problem - but there’s an art to this.
Google is a great resource, and I’m sure you’ve used it not a few times going through this book. However, blindly looking up ‘how to write a class in C#’, then copy/pasting code from the top result, then shooting off a quick prayer before running the code isn’t a good way to work! You need to be systematic in your approach.
Don’t just take the top result from a search engine as the absolute truth - take time to read through several results and think about what each one is saying. Perhaps the answer is a combination of different things from these sources. Sometimes there is no ‘correct’ answer to a problem - only different answers with their own pros and cons.
One helpful resource when you’re stuck is the official C# documentation. It’s is written by the people who created the language - so they know what they’re talking about. When you’re stuck, this should be the first place you look. You can find the official Microsoft C# documentation here: https://docs.microsoft.com/en-us/dotnet/csharp/.
The down side is that it can be quite overwhelming for beginners. Even professional software engineers can spend a lot of time wading through the vast amounts of information in order to find the bit they’re looking for. You need to be careful and systematic to find the information you need.
Forums and discussion sties - notably Stack Overflow - often provide a good source of information for beginners. You’ll find people of a range of experience asking questions, and providing answers. The likelihood is that any question you have already has been answered at least a couple of times on Stack Overflow. So make sure to look carefully as you may find what you’re looking for here.
Also, a final note - programming is hard. Sometimes you get frustrated and things don’t work for seemingly unknown reasons. You have to persevere!
What I’ve explained in this chapter is a very broad and general overview of the discipline of software engineering. Each topic is really enormous, and it feels even a little strange for me to give such brief descriptions of them. But that’s all fine - because you’re taking your first steps into becoming a true software engineer!
In the next Kata we’re going to look at how to put all that stuff into practice! This is going to be the most challenging Kata, as it won’t provide direct instructions on what code to write. Instead, you’ll be given a business problem and a requirements specification, and be challenged to write a C# program from scratch to solve the problem. You’ll also have a set of test cases that your program needs to pass in order for it to be complete. You’ll also have to figure out on your own how to do things in C# that this book hasn’t covered before!