Seminar 12 The skill of programming

12.1 Writing the code

Programming is not about writing code that works. That, obviously, must be true but it is only the minimal requirement. Programming is about writing a clear easy-to-read code that others and, perhaps even more importantly, you-two-weeks later can understand. Below are several suggestions on how you can improve your code.

  • Use a linter. In VS Code, press Ctrl + Shift + P and start typing Python: Select linter. You will be offered a number of choices, pick pylint (that’s the one I use) or flake8 (it is more strict). VS Code will offer you to install the actual linter package, if it is missing. Then, the linter will analyze your code after every save and highlight issues it finds: spaces where should be none, no spaces where should be some, wrong names, trailing spaces, overly long lines, etc. Try to address all the problems as it will make you code look consisten, “standard”, and, therefore, readable. However, use your better judgment because sometimes longer-than-linter-likes line is more readable than two shorter ones. Similarly, a “bad” variable name by linter standards can be a meaningful name for a psychologist. Remember, your code is for people, not for the linter.
  • Document your code. Every time you create a new function: document it. New class: document it. New constant: unless it is super clear from the name alone, document it. First, VS Code is smart enough to parse Numpy docstring on the fly, so it will show this help to you whenever you use your own functions. Second, it forces you to think and formulate (in human language!) what the function is doing, what type the arguments are, what is the range of valid values, what are the defaults, what should it return, etc. More often than not, you will realize that you have overlook some important detail that is not apparent from the code itself.
  • Add some air. Separate chunks of code with some empty lines. Think paragraphs in the normal text, you wouldn’t your book to be a single paragraph nightmare? Put a comment before each chunk that explains what it does but not how it does it. E.g., in our typical PsychoPy-based game there is a point when we draw all stimuli and flip the window buffers. That is a nice self-contained chunk that can be described as # drawing all stimuli. The code provides details on what exactly is drawn, what is the drawing order, etc. But that single comment will help you understand what this chunk is about and whether it is relevant for you at the moment. Same goes for # processing key presses or # checking gameover conditions, etc. But be careful and make sure that the comment describes the code correctly. E.g., if the comment says # drawing all stimuli where should be no stimuli-drawing code anywhere else!
  • Write your code one teeny-tiny step a time. Your motto should be “Slow but steady”. This is the way I guide you through the assignments. Start with a something simple like a static rectangle or image. Make sure it works. Add a minor functionality (change in color, position, another rectangle, storing it as an attribute, etc.). Make sure it works. Never go to the next step unless you fully understand what your current code is doing and you are 100% certain that it behaves as it should. This tortoise-speed approach may feel silly and overly slow but it is still faster than writing a large chunk of code and then trying to make sure that it all works. It is much easier to solve simple problems one at a time than a lot of them simultaneously.

Unfortunately, these tricks won’t work it you do not use them! So you should always use them and they should become your good habits, like using a seat belt. It does nothing on most (hopefully, all) days but you wear it because it might suddenly become extremely useful and you can never be sure when this will happen. Same with coding. Quite often you will be tempted to write “quick-n-dirty” code because this is just a “simple test”, temporary solution, a prototype, pilot experiment, etc. But, as they say in Russia “There is nothing more permanent than a temporary solution”, and you may find that your toy code grew into a full blown experiment and it is a mess. Or you want to come back to that pilot experiment but realize that it is easier to start from scratch than to understand how that monster works8. Thus, resist the temptation! Form the good habits and you future-you will be very grateful!

12.2 Reading the code

My experience with programming in general and on this seminar in particular is that most problems you get stuck with are simple (to be point of being dumb) and obvious in retrospect9. Unfortunately, this knowledge is of little comfort while you are trying to figure out why the bloody thing does not work! Here are several suggestions that could help you to resolve them faster.

  • Think like a computer. Read the code line-by-line and “execute” it the way the compute would. Use pen-and-paper to keep the track of variables. Trace which chunks of code can be reached when. Slow yourself down and make sure you understand each line and are able to keep track of the variables. Once you do that it will be easy to spot a mistake.

  • Pretend that you’ve never seen this code in your life. Assume that you have no idea what it is doing. Quite often you literally do not see a mistake because your brain fills-in details and bends the reality to match your expectations. You know what this chunk of code should be doing, so instead of reading it you skim through it and, unless it looks obviously terribly wrong, assume that it does what it should. This is not just programming-specific thing but a general feature of our perception. If you ever proof-read your writing for typos, you know how mind-boggling difficult it is to spot them10! Turning your expectations off is hard but is immensely helpful.

  • Use the debugger. Put breakpoints and execute the code step-by-step. Check values of variables using “Watch” tab. Use debug console to check whether functions return the results that they should. For complex conditions or mathematical formulas, split them into small bits, copy and execute these bits in the debug console and check whether numbers add up. Make sure that a code chunk checks out and then proceed to analyze the next one. Debugging is particularly helpful to identify the code that is not reached or reached at the wrong moment.

12.3 Zen of Python

I also found Zen of Python to be good inspiration on how to approach programming.

  1. Happened to me more often than I dare to admit.↩︎

  2. Hindsight is always 20/20!↩︎

  3. Advice, read each paragraph backwards: Last sentence, penultimate sentence, etc. This breaks the flow of the text and helps you concentrate on words rather than on the meaning and the story.↩︎