As an engineer, your future can be blessed with working on profound,maybe even life changing technology. That is,of course,if that is what you want your career to be! You might find yourself working on the latest pandemic iPhone App, the most hotly awaited virtual reality based video game, an Earth shattering AI algorithm capable of predicting the next stock market crash, or even a defense shield for weapons of mass destruction. The future could be filled with exciting and rewarding opportunities, let’s hope so ! But take a step back.How do you make sure whilst creating such imperative, progressive applications and services, that you are producing the highest quality of work that you can ?
Quality is a word that baffles us all in terms of its definition. As engineers we spend our lives chasing this ephemeral characteristic, watching it elude us as it teases us with its super powers of morphology, and its enchanting displays of figurative gymnastics. It overwhelms us when used as a yardstick for progression in novels such as Zen and the Art of motorcycle maintenance. What is it ? Why is it drenched in relativism, subjectivity ? If only we could define it, measure it, use it to predict a systems effectiveness, reliability, adaptability, suitability, correctness, state of readiness. But alas,we can not. In the nascent field of engineering, quality is at best a guess, and at worst a false positive, plastered with misleading statistics, and overly dependent on ambiguous semantics. Nobody,as yet,has definitively produced a useful,empirical,trustworthy device that allows us to measure the quality of a computing system.
So with this aforementioned lack of ability to define how good our work actually is,it is up to us,as engineers to strive towards producing the best quality we can,even in this absence of the ability to measure quality. We need to at least take steps to ensure we are covering all the obvious signs of a reliable, correct and effective system. What this means,when boiled down to its bare bones, is that it is up to you as an engineer to push yourself to produce the best quality of code, the best design and the most effective testing you can. This is why as an engineer your commitment has to be to excellence. This commitment to excellence is twofold. It is up to you to drive yourself forward in a carer, that if you are not careful,can be full of cul-de-sacs, dead ends, stagnation and “lazy contentment”. You must constantly be pushing yourself, first to be an excellent programmer, then an excellent designer, then analyst,but in parallel to all this,you must also be constantly seeking new ways to improve measuring the quality of what you are producing. The first part therefore is easy, it’s down to effort and hard work,keep learning as much as you can,experiment,tackle things that terrify you,learn,adapt and grow.Learning does not stop when you leave university,computing languages for example are constantly evolving,this years new hot features are yesterdays fish and chips!The second half in the quest for excellence, is how to measure quality,and this is more tricky.
Most places that I have worked follow the predictable workflow of requirements capture,analysis,design,implementation,test,delivery etc. Depending on the size of the company one is working for,most of the time the role you have as a software engineer will be limited to the areas of analysis, design, implementation and functional testing.You will start with some user or system requirements,from that you will design a system with some prototyping to solve the given problem,then you will implement it with software.Next you will deliver that code to a repository like Perforce or Git, and then Quality Analysts (QA) will take that build of your system and test it. They will begin with the obvious tests, so no crashes, make sure the functionality of the system is exactly as defined in the requirements etc, then with lower importance,look for bugs,side-effects,aesthetic or cosmetic mistakes etc. These undesirable incidents are recorded with some kind of bug,or software issue system, such as Jira.The engineer will be allocated these issues,fix them all, check these fixes back into the repository,and the process repeats, until eventually the software is released.
The longer a bug or undesirable effect of a system survives undetected, the more expensive in terms of cost and effort to rectify and deliver it to users becomes. For example consider a software crash when the system has just been delivered to QA,it’s 5 months before release,it will be easy to get the fix into the system, the engineer changes the code, delivers it to the repo, QA retests and verifies it’s fixed, and the bug is closed, no users or customers in the equation at all. Now consider the system is installed on an aircraft, the bug is detected, it has to be reported back through customer service, verified with QA, the engineer that wrote the code left last month so another engineer has to come in, fix what is broken, deliver it, it goes back through to QA etc, then it has to be delivered in a firmware update via the internet etc. You get the picture.It is much better to capture bugs,mistakes and broken features when the engineer is writing the code, then it is when the plane is flying through the air to Mexico! Now, obviously we are talking about a subsection of the systems quality, after all you can not test the interaction between the radar system and the landing gear of an aircraft sitting at your desk with Visual Studio and a simulator! But you can test your subsection of the system you are working on. Incidentally,the difference in testing areas here is between system tests (testing the whole system with all components), as opposed to functional testing (which could be testing to make sure that a specific function changes a system state when it is called,as intended). I will be covering the different types of software testing in another blog soon !
So,there are many tried and tested methods of measuring the quality of a software system,the best of which seem to be the ones that are generic enough to adapt to the plethora of contexts that present themselves in the real world.An important area of growth in our industry in the last ten years has been the rise in popularity of Test Driven Development. TDD can be a fundamental shift in the way software engineers work. Often enshrined in their quest for efficient code, software engineers sometimes need to be knocked out of their comfort zone and forced to adapt to a new practice,and TDD has certainly been one of these situations for those us working in the industry since before it arrived.
It is fundamentally a new way to work in developing software. Most of us who have worked in the industry for a long time pride ourselves on our ability to prototype good working code, and produce it quickly. Then we spend time optimizing (always optimize at the end of development BTW !), and improving efficiency, making sure our web services and DBs scale for expected user numbers etc. So it came as a big shock to many of us, to take a step back, and rather then writing the code first,then the writing tests to make sure it works as expected, this new working method called TDD turned this on its head. The tests were written first, then the code was written. Those of us that could adapt quickly saw the enormous benefits from this approach to software engineering,we did it with everything, all types of application, all types of service! However it soon became apparent that as with most things,it is a great step forward,but it is not perfect,but it is not a panacea!
So what is Test Driven Development ?To recap,we used to implement the code,and then we would manually test it. There would often be no record of any test results, test code coverage etc.To manually test the code we might be lucky and the system we are testing might have a GUI (graphical user interface),enabling us to click on the buttons and watch the results right there on our desktop.Or if the system was more complicated,we run a series of automated functional tests, i.e. call the function named SumOfTwoNumbers,passing in two integers,and test the result In this case execute the test that will call the Sum function with 7 and 7, making sure we get 14 ! So we write the code then write the test code and execute it.Engineers, as we have discussed in previous blogs tend to be of the frame of mind, “get in get out”, “leave no footprints” etc.Roughly translated in testing this means..”leave it to QA”. Yes engineers, especially software engineers tend to hate writing tests. It is an area that doesn’t require any flare,and is often spent laboriously ploughing through requirement specs looking for edge cases and writing test code that is very mundane, uninteresting and is not even included in the final product.It is an after-after-after-thought.
So in terms of reward from writing tests, there aren’t any. You will not have a peer come running up to you with incredulity bursting from their happy face,stuttering the words “That …that..that test you wrote, OMG it just blew, I mean it blew me away”, but you might have that same peer screeching out the often heard “That optimization of the breath first search for the ant farm inspired, swarm intelligence traveling salesmen problem….”. So you can see where the narcissistic rewards might lie. It’s not that engineers dislike quality, en-cont-re, they love quality, they just hate the mundane, and documentation and testing seem to fall into this category.
So, how does TDD help this?The best thing about TDD is that it comes first. The chicken finally comes before the egg. TDD requires that the software engineer first understands the requested requirements,specifically what the system must do,then after this has been ascertained,they start the implementation, but not by writing the code,no,by writing the code that will test the code.This test code will prove the software does exactly what the requirements state. There should be no ambiguity, no randomness etc.The system should perform exactly as required, in a deterministic fashion (so repeat the execution with the same environment,context,data and always get exactly the same result). The system should also not crash, or have any undesirable side-effects, or intermittently fail, and it should also handle legitimate system failures correctly (i.e. if there is no file called ReadMe.txt then tell the user, don’t sit there with a busy cursor waiting for the file to appear on disk !).
Let’s continue with the above example, if we know that we need a function that produces the product of 2 numbers,then we write a test first, called TestProductOfTwoNumbers. This will then call the new function called SumOfTwoNumbers.The test code will call the function,passing in two integers and make sure that the returned result is correct.The result of the test can be logged into a test report (often linked to a web portal).When the test passes,we move on to the next function to test and implement.
Wow!This is much slower development than before, and you can feel like a simple system that used to take a few hours to knock out is now taking a whole day to implement.But,of course this is short term observation.Remember that the longer a bug survives in the system, the longer it takes in the future to fix and deliver it.So as engineers we need to find and fix bugs before they leave our desktop.Bugs can also force other developers who use our systems, to work around them,inserting code to make their system work,this is compensating for the fact our system is not working as expected.Then,in the future when we fix our code,their system starts failing!So many problems arise due to software bugs, so any step towards writing better code should be fully embraced.When writing code in this way,we as developers are forced to think about exactly what the system should be doing.This approach very quickly opens up any holes or ambiguity in the requirements, forcing us to seek validation before writing any code.
Most of us who work in Windows development use the ubiquitous Visual Studio. Built directly into their VS IDE (Integrated development environment) Microsoft have added support for the Microsoft MSTest framework. This gives the programmer a direct IDE based testing framework, and it adds plenty of C# annotations that extend the language offering lots of nice features needed to test the code. The developer can also run the tests from the IDE, before submitting their code, the tests are run during development by the developer,then after submitting the code the tests are run etc.Every time a piece of software is changed the tests are run.Thus making sure that when any code is committed to the repository it is not breaking any of the other existing code in the system.TDD makes this testing become part of the development cycle.Write the test first, write some code, make sure the test passes, and continue,ad-infinitum (or at least until we have finished !)
TDD is not the only approach we can use to improve code quality.There is also continuous integration,full regression testing,and even agile development. All these help us to improve software quality and responsiveness when developing software.In this blog today we have focused on one aspect just to illustrate the point,that sometimes in your career as an engineer, you might have to completely revolutionize the way you work in order to become better at what you do.Embrace these opportunities,as we will all benefit from them,especially those of us sitting on that plane to Mexico!
Happy New Year !!