By Markus Gartner
Do you know the joke about how to become an expert in C++ in 21 days? You learn the basics in days 1-21, and continue to the advanced topics in days 22-3648. Then you learn enough about physics and biology to build a time machine, and an age-reversion potion. Then you travel back to day 21, and replace your older self with your younger self. Some testers find it’s similar if they want to learn programming.
A while ago I read a traditional book on testing. One thing stood out for me. The authors mentioned that testers can benefit from having some knowledge about programming. I certainly agree to that statement. Having run a couple of BBST classes, though, I often find that testers from a non-technical background may find that advice difficult.
Psychologically if you are a tester, and you don’t want to learn something about programming, no article, training class, or coaching will help you. That said, don’t feel forced to learn programming. You don’t have to. Just be aware that there might be some bugs that you won’t find without some knowledge about programming. If your testing team can compensate for that, that might not be a problem.
Also be aware that you need to be open regardless of your past. You won’t learn much when your thoughts constantly have to deal with how difficult this is, and that you failed in the past to learn something about this. If you can let go of those feelings about the content, the learning will stick more.
Some people are even oblivious that they are programming. Have you ever played with Excel in order to derive some information for you? Maybe you put in some formula in the cells in your spreadsheet. That was programming. I remember that I once wanted to write a tiny application in VB script for Word that could help me write training plans for my swimming classes more easily. That was fun in the beginning – and a drag in the end because I was oblivious that I was programming. Eventually I abandoned that pet project.
Finally, you should be clear on why you want to learn programming. It might be in order to find more bugs that you currently miss now. Or you might want to learn programming so that you can automate some of your tedious tasks. Or you might want to learn more about programming because you want to be able to talk to your programmers on the same level. Be aware that these reasons may lead to different approaches.
Before we dive in, one final piece of caution. I studied computer science, and as a side-effect learned some programming back in university. That said, I might or might not be the right person to tell you things about programming.
Your first language
When learning something new, I found timely feedback crucial. As a swimming trainer I was told that kids keep the experience of their movements in mind for 30-45 seconds. That means that I as a trainer need to correct a wrong movement within that timeframe. It’s the same when learning programming languages.
That said, for your first programming language, you should pick one that provides you quick feedback. Nowadays scripting languages are wide-spread. These most often come with an interpreter. In an interpreter you can type in programming fragments, and execute these statements directly, and get instant feedback. That enables quick learning for you, especially if you are completely new to programming.
During my university time I spent much time on the IRC. Back then, I used an IRC client for chatting called mIRC. It came with a scripting language, mIRC script. While hanging out on countless channels chatting, I also started to improve my IRC client, so that some things were easier to solve. Thereby I learned a lot about programming, how to build maintainable functions, and other concepts. For example I created simple commands for quick access while chatting. In order to post the song I was currently listening to, I just had to type ‘/mp3’, I had created a dialog to play sounds to the current channel, and so on. Oftentimes I had lots of intrinsic motivation for these pet projects. That is important to keep in mind when taking your first steps in a programming language. If you can find tiny projects on your own, you will find more progress learning the language.
Among the set of scripting languages available, I recommend languages like Python, or Ruby. These come with a large set of standard libraries that make your life easy. Although in the beginning it might feel overwhelming, over time you will become familiar with those libraries, and can solve increasingly complex problems. I remember having read an article about how to create your own answering equipment at home. That project involved Python programming. I extended the existing source to write call logs to a database, and created a tiny php application to read contents from the database, so that I could check call logs at home when I was somewhere else. At some point I included abilities to listen to the call messages through the browser. That was fun.
In general there are two strategies that you can use in order to get familiar with a particular programming language. You can either dive in the language references or tutorials, and work through them. I usually found that I learn more when I start with an existing code base, and try to extend it. At that point my learning will be more targeted towards solving a particular problem. I will still need to dive into the language reference, but will have fun solving an easy problem.
With the interpreter in the back, I have a playground to try out statements, and test them before putting them in the production code. That eases my learning curve, and I can progress more quickly. If you can find a pet project like answering equipment, you will learn more quickly. Oh, one piece of advice while taking your first steps in a programming language: don’t stop learning when you get frustrated because you can’t solve a particular problem. That usually meant for me that I was on the edge to find out how it works. Sometimes I was working until 3am in the morning, becoming more and more frustrated. After a night of sleep, I had found the clue that I needed in order to solve that problem. It’s still the same today.
One final concern: how deep should you dive into the programming language? Learning a programming language can quickly become an endless journey. Don’t dive too deeply into it. For a first grasp of programming it will be enough to understand basic concepts like functions, variables, constants, and the basic data types. On the data types, you should familiarize yourself with strings, arrays, and maps, as well as various forms of numbers, and how to translate either into another. These concepts are included in almost any programming language, and you can be productive with these on simple projects quickly. Also learn the basic functions for those data types. For arrays, you can put stuff into it, get its length, and extend it. With these functions you should have a solid go. You should be able to automate some of your repetitive tasks easily with this knowledge, and become even more productive at work.
Low-level computer organization
If you are working on a highly technical product, then you probably expand some of your knowledge by learning more about the foundations of computers. These days many programmers are not familiar with the underlying foundations in computers. For example, most have never dealt with limitations of floating point arithmetic, assembler languages, and pointer arithmetic. While these topics scare some non-technical testers, they can be hugely valuable in order to build an understanding computer programs – and can split the respected testers from the not so respected ones.
Elisabeth Hendrickson told me a story about a tester she once met. The tester tested a web application. She used the mouse, and moved the scroll-bar up and down. Hendrickson asked her, what she was doing there. She said, she was testing the application’s scroll-bar – oblivious about the fact that any bug she could find with that would be a bug in the browser, not the web application. Can you imagine how a programmer would react upon a tester reporting a bug in an application that clearly was more part of the browser in use rather than the code that programmer wrote?
If you would like to avoid exposing your ignorance to your programmers, you should dive deeper into topics related to lower-level concepts. Do you remember the Y2k-bug? A few years back the whole IT industry poured enormous efforts into changing the underlying date representation from two digit years to four digit years. The fear was that computer system could not distinguish between 1900 and 2000 once the near century had started. Skip forward a few years, and it seems that these problems have been long forgotten. At times, I joke about the problems that the Y10k bug will bring in the future. Can you imagine how many people will be familiar with the underlying computer organization then?
If you dive deeper into the internal data structures in a computer, and lower-level programming, you will find many opportunities to find bugs. For example, the computer uses a fixed amount of binary digits to store numbers internally. This causes some problems when translating from binary numbers to more human readable decimal numbers. One example for that is the internal date representation found in many Unix systems. Dates are represented as seconds since the 1st of January 1970. Unix systems store this value in a signed 32-bit integer. Signed 32-bit integer values range from −2,147,483,648 to 2,147,483,647. This allows the Unix time to also represent dates before 1970. Unfortunately since limited precision numbers in computer can overflow, this also means that there will be a date and time, when the Unix date will overflow from the largest possible number to the lowest possible number. In the current implementations of the Unix date system with 32-bit signed integers, this will happen somewhere around 2038. That’s quite a bit earlier than the Y10k bug. If you realize that many of the Internet servers are running Unix systems, the Year 2038 problem might even become relevant for the system you are currently working on.
Consider the Network Time Protocol (NTP). That is used in order to set the right date and time. A timestamp is fetched from a server on the internet, and a local NTP program adjusts the time of your computer on regular basis. This system is widespread among Apple, Windows, and Linux systems. These systems experience similar problems as the Unix Year 2038 problem, resulting in an overflow to occur in the Year 2036. Are you sure your system will not be affected by that?
The basis for all these problems lies in the internal representations of numbers in computer systems, and the problems that occur when converting binary numbers into decimal ones. A few years back I worked with a large telecom application. There were two larger subsystems that exchanged data between each other. With some knowledge about binary to decimal number conversion, and floating point representation, I was able to find a bug that could occur on a particular date, resulting in miscalculation of fractional digits. That bug could have led to customers paying too much of their money to a telecom provider in the range of a couple bucks. Summed over 10 million subscribers, there could have been a serious problem.
Low-level data types are not enough, though. When it comes to programming, you should know assembly language as the building block for today’s programming languages, and where more structural programming language like object-oriented programming differentiate. Why does this matter? Consider some of the test design practices that you might use on a daily basis. Did you know that most of them were discovered when bugs were introduced in more procedural programming languages, or assembly languages? So, how do you know they still are relevant when dealing with object-oriented programming like it’s widespread nowadays?
When dealing with compiled languages, the compiler translates higher-level programming constructs into lower-level programming constructs. Classes become blocks of code in low-level assembly language. Nowadays these compilers rarely create surprises in the underlying machine code. So, how are problems created in our programs? When you know the limitations of low-level assembly language, and how more modern programs might be translated into the machine language, you also become more familiar with the limitations of your test design techniques. You do not have to thoroughly understand these concepts, nor have to be able to program in assembly language. But to become more effective as a tester, you should know about the underlying principles.
Unfortunately covering such a topic as assembly language and binary numbers is too much for this article. Back then I read some very good books that covered the basics of computers. These started with “Write Great Code Volume 1 – Understanding the Machine” and “Write Great Code Volume 2 – Thinking Low-Level, Writing High-Level” from Randall Hyde [Hyde2004, Hyde2005]. Some of the underlying basics about how your computer is organized is covered in “Computer Organization and Design – The Hardware/Software Interface” by Patterson and Hennesy [PH2013]. These books helped me connect back with the underlying organization of the computer. Of course, nowadays there are also resources like wikipedia, and online reading material that might help you get a deeper understanding on low-level computer organization. Also early textbooks serve a huge foundation on the fundamentals of programming.
If you have dealt with some programming languages, and became more and more familiar with the underlying concepts, you might have started with some automation of your tests. Since test automation is software development, you probably need to dive deeper.
I remember a test automation experience where we as a group of testers felt a large bit of pain. The automated tests were unreliable, slow, and needed lots of analysis after they ran. Eventually we figured out that it was the design of the test automation was causing us most of the pain. Back then, I started to dive into programming constructs like design patterns, refactoring, and unit tests. These helped me largely to re-write the whole test suite to a degree that was still in use three years later. The understanding of software design helped me to construct simple components that could be reused in several occasions, and see where code duplication previously had caused us a lot of pain.
In order to apply design patterns in your automation code, you should make yourself familiar with the concepts of object-oriented classes, interfaces, and functions. Design patterns will teach you how to tailor your classes so that you can reuse commonly used code more easily. For example, I remember that we applied the Strategy pattern quite often. With that pattern, you have several strategies how you can accomplish a particular task in your working code. These strategies can then be plugged into your test automation code on demand, and be unit tested in separation of the whole application. You start to build an automation code base that allows you to plug-in different behavior thereby leading to easier reuse of concepts.
One piece of caution: learning how to apply design patterns constructively takes some time. Also, you need to teach your colleagues on these patterns, since they provide some sort of indirection and abstraction that people working with the code need to get used to. In the long run, getting more familiar with these patterns though will serve you well if you can create a shared understanding of the underlying structure.
No matter what, only applying design patterns is not enough. Over time code bases tempt to rot. They need constant care. This care comes with refactoring. Refactoring means you need to restructure your code without changing the outside behavior of the code. With refactoring you can transform an existing code base from one structure to another one. Thereby you can change your code from one design pattern to another.
There are many books and resources out there on design patterns and refactoring. You can almost pick any one, and get started quite quickly. However, also consider working together with a programmer on these. A programmer can coach and teach you about his thinking process while transforming the code base from one pattern to another one. You will also benefit greatly from his experience in applying these patterns to production code.
Starting programming can become a never-ending road. It also appears to be a long road. Be sure to get started with the first step, and don’t get overwhelmed by all the stuff out there that you could learn. Focus on a single piece. Focus on the stuff that brings you the most fun. Thereby you will learn more quickly.
It seems in order to get familiar with programming, you should look for a couple of things. Start with finding a pet project that solves a practical problem for you. It must not have anything to do with your work as a tester in the end. But it should solve something, and you should have fun solving that problem. That in combination will help you take the first few steps.
Also make sure to solve a tiny problem for your own, and refuse to sell that solution to someone else. Your first programs will be tailored for you, and only for you. If you try to install the same solution for someone else, you will quickly find out that there are many things these folks do differently. That might cause you pain, since you eventually need to rewrite your whole solution to incorporate safety checks, and avoid problems that you wouldn’t have run into on your own.
These tips should get you started. With some basic understanding of programming, you will figure out not only where bugs might lurk. If you start programming yourself, for example on your pet projects, you will find out how you think differently as a programmer. With that in mind, you realize how bugs slip into a program. Also you will find more empathy with your programmer colleagues. That way you will find different ways to connect to your colleagues, and convince them more easily to fix a particular bug in the program. Sounds like a win-win situation for both of you.
As Morpheus said in the Matrix movie: “I can only show you the door. You are the one that has to walk through it.” I showed you some doors. Decide through which you may want to walk.
[Hyde2004] Hyde, Randall; Write Great Code Volume 1: Understanding the Machine; 2004; No Starch Press
[Hype2005] Hyde, Randall; Write Great Code Volume 2: Thinking Low-Level, Writing High-Level; 2005; No Starch Press
[PH2013] Patterson, David A., Hennesy, John L.; Computer Organization and Design: The Hardware/Software Interface, 5th edition; 2013; Morgan Kaufmann Publishers In