How I learned programming
In this post I will tell the story of how I learned to program. I took heart from other accounts by career programmers — it is worth restating that programming comes easily to almost nobody, and that there is no substitute for practice. I would also like to acknowledge the people that helped me develop a useful and intellectually rewarding skill.
Of course, I should add a disclaimer that I still do not consider myself a good programmer. I know enough to use it pretty well as a tool in my research, but I am not a professional coder.
My disclaimer has two important implications. First, it is worthwhile to learn to code even without any ambition of becoming a software engineer, because the skills can be deployed in almost any technical field. Second, our sense of being good at something is a strong function of our present ability in the same skill! So do not be disheartened if you feel you are not making progress. Objectively, you may well have done, but your standards have risen too.
Turtles
My first memory of the concept of programming goes back to age eight. In a special one-off schoo lesson, we played around with ’turtle’ robots. These were motorised objects that could be instructed to go forward, backwards, and rotate in the LOGO programming language.
I had already been exposed to computers: there was a computer (just one!) in each classroom, and we had a computer at home. I was certainly fond of computer games. Looking back, however, the turtle was important because it was a hint of the generality of what computers can do — subject to floor space constraints, we can instruct the turtle to take any path we like, as long as we express it as code.
BASIC on the Acorn
My grandfather worked with computers in the early days of the field; in fact, he estimated, he was one of the first 100 people in the UK to program a computer. Having witnessed the rise and fall of the British computer industry, he had an affinity for Acorn computers. As I recall, his collection include two Acorn RISC OS machines, in addition to a Pentium 75MHz machine and a modern laptop for internet access.
A few years after my turtle experience, he suggested I might like to try some
programming. He showed me a BASIC
program that took two integers as input, and printed out their sum. I remember
it being explained to me, that a PRINT
statement had nothing to do with the
(dot matrix) paper printer connected to the machine. I also remember what I now
know as ‘sigils’, where symbols are added to variable names to indicate their
type, for example var$
is a string and num%
is an integer. Fortunately
this notation has fallen out of use in modern languages.
By the end of the afternoon, I had written a calculator program which offered a menu of which arithmetic operation to perform, took the operands, and printed the result.
If I wrote any other BASIC programs, I have forgotten them. Although I did not develop a sustained interest in hacking away on RISC OS, my BASIC calculator was the seed that eventually grew to where I am today. It was my introduction to the concept of variables, if/else branching, and the universal debugging tool that is a print statement.
Games
I have alluded to my youthful enthusiasm for computer games. In particular, I liked customising the games and trying out mods. This required some familiarity with the file system, editing configuration files, and so on. Apart from an unsuccessful attempt at writing a mission for Grand Theft Auto 2 in a proprietary scripting language, there was no programming involved.
I received DARKBASIC as a Christmas present. This was meant to be an accessible way for beginner programmers to make games: a BASIC-like programming language with an interface to DirectX for 3D graphics. I quickly worked my way through the tutorials, rendering a spinning cube and so on. But, after that, I was overwhelmed with the universe of potential possibilities and amount of work involved in writing a game from scratch. I did not know where to start. I don’t think it occurred to me to start as simple as possible, split into small tasks, and build things up incrementally, which I now know to be a sensible approach.
If I was just a bit older, with a bit more programming experience, perhaps I could have made something playable. But the reality is that all of the commercial games I had played were many man-years of professional developer effort. No matter how user-friendly the language might be, a 3D game does not make a good first programming project.
Websites
During Sixth Form, I developed an interest in making websites. I wrote them using hand-crafted HTML and CSS, an approach that was going out of fashion then and is almost unheard of now. Things have got much easier, particularly with the growth of static site generators (as the present website is rendered), or the Wordpress ecosystem for the slightly less technically minded.
Although I would still not describe writing HTML and CSS as ‘real’ programming, the edit-refresh-think cycle is quite similar to software development. We have to be precise and patient in order to iterate our web page into the look we want. I used a combination of on-line resources and books to get the knowledge I needed. Again, these are programming-adjacent skills that indirectly contributed to the overall learning process.
I was much more successful at building websites than making a game for a couple of reasons. The nature of HTML and CSS means that we can lay out a skeleton of the web page straight away, and gradually add styling. Iterating on a CSS rule is very fast and only involves refreshing the web browser, so we can reach our goal within a short space of time.
Linux
Also during Sixth Form, I got the first computer of my own, a small netbook with Linux pre-installed. I spent a lot of time playing with the operating system, configuring it just as I wanted and getting my favourite games to run under WINE. The device had an active community online, and I followed a guide to install packages from main-line Debian onto the stock Xandros distributions. Eventually I ignored the warnings that packages should be mixed with caution, broke something, and rendered the OS unbootable. I tried out a few other distributions after that.
I quite liked playing around with Linux, so I later built my own computer to use as a server. I think I served some kind of silly website over my home broadband using dynamic DNS with a port forwarded through my router. I ran a web-based BitTorrent client and used it to download stuff independent of my primary laptop.
Once again, despite plenty of opportunity, I did not do any ‘real’ programming on my new machine. But I did get to know my way around Linux and learn a little about networking.
Dorchester Software
Let’s recap the story so far. I have been tinkering with computers for perhaps five or six years, but discounting several brief and failed attempts, have not done any actual coding. I know about a lot of the abstractions I will need (what is a file? what is a variable?) but have never put the pieces together on my own.
Between Lower and Upper Sixth, I spend a summer ‘helping’ with my uncle’s one-man software shop. I sat down and watched a tranche of video tutorials on Microsoft C#, and worked through their exercises. By the end of the summer, with some guidance, I was able to fix bugs in the existing codebase.
I would describe that summer as my first proper programming experience. In a matter of weeks, I was able to transition from the structured realm of tutorials to real programming. But, without the priming of previously playing with computers more generally, I don’t think I would have been any use as a software intern.
Looking back, I’m particularly grateful to my uncle for giving me this opportunity, and aware that most people would struggle to find a software company willing to take on a totally inexperienced teenager. Perhaps the communities around open-source software would be a good substitute place to start: small and manageable bug reports to work on, feedback on your work via pull requests, and judgement by your code not your credentials.
Undergraduate
The distribution of programming ability amongst incoming undergraduates to the University of Cambridge Engineering Department was strongly bimodal: those that had not done any coding before coming up, and those who had. Falling into the latter category, I found the Computing coursework fairly straightforward.
Although I learned the rudiments of many new languages — I was exposed to Matlab, C++, a little assembly, and Fortran — I don’t think I improved much as a programmer. Owing to the time constraints of term and the other courses we were taking, each project was very structured, short, and shallow. With hindsight, I should have taken the Software Engineering module in fourth year.
The Computing curriculum has been improved since my time. All labs have standardised on Python, which is easier for beginners, widely used in industry and academia, and free software. The students do a compulsory group project in second year which looks to be quite deep. Version control and unit testing are introduced early on, whereas I had to teach myself once I started my PhD.
I used Matlab for data analysis on two paid summer internships (my first programming in exchange for money), but the code I wrote was objectively bad. I knew how to hack together a one-off script that did what I wanted, but knew nothing about how to write reusable, reliable or maintainable code. I had a bad habit of generating similar plots for different data by copying a script and changing the input file name, despite well knowing how to define a function!
By the time my fourth-year project supervisor introduced me to Python, I gave it a quick go but ended up rewriting the code he gave me in Matlab. Somehow, I was blind to the existence of NumPy. I remember struggling to multiply every number in a list by a constant factor, and trying to index into a two-dimensional array made from a list of lists. In native Python these look like:
y = [xi*c for xi in x] # Multiply every element of x by c
A[i][j] # Access the element at ith row and jth column
but I preferred the Matlab syntax
y = x.*c % Multiply every element of x by c
A(i,j) % Access the element at ith row and jth column
I might have stuck with Python if I had known about the NumPy way
y = x*c # Multiply every element in numpy array x by c
A[i,j] # Access the element at ith row and jth column of numpy array A
PhD
During my PhD, I wrote a lot of code: for setting up, running and analysing computational fluid dynamics calculations; making simple mathematical models; and data acquisition and processing for an experiment. Unfortunately, most of it is undocumented, specific, and not easily reusable (even by me!). So, apart from the learning experience, it had no utility after I submitted my thesis.
Matlab seemed to be the most used language around the Lab, with a lot of common functions already available, so I went with the crowd and my existing knowledge.
I used Git to version control code, but only committed once a week or so with a vague commit message. It basically functioned as a backup, rather than a useful record of changes. There were several occasions where I could not reproduce my own past results because of broken code, which version control should prevent.
To start with, expressing my ideas in code was the dominant obstacle to making progress in my research, but by the end of the PhD coding was the easier part. I think this just came from practice with many different programming tasks. Each problem was individually quite small, did not have a set answer, and would take an afternoon or a day to solve.I started to develop some taste for elegant and well-structured code, but what I wrote usually failed to live up to these aspirations.
Post-doc
By the end of my PhD, I had become frustrated with Matlab — I felt that it language did not encourage the writing of good code. For example, each function has to be in a separate file to be accessible from other functions, leading to overly long functions or a proliferation of files. With an eye on ’transferable skills’ for a life after academia, I was also a bit uneasy about becoming an expert in a proprietary language not often seen outside of Engineering. Real software is not written in Matlab, I thought.
The first task of my post-doc was to port 4000 lines of Fortran into another more convenient language, and I chose Python. One of the aims of the project was to end up with a computational modelling tool that could be given to my industrial sponsor, and Python being open-source is easier to distribute and install.
I picked up the syntax and way of working in Python fairly quickly. Python is now my preferred language. The existing ecosystem is so convenient, and there are ways around the slowness if you end up doing some heavy computations.
I enjoyed the new intellectual challenge of structuring a large program to achieve a set of features as a cohesive whole. Writing software to be used by others also entails writing documentation. I actually found writing documentation quite satisfying, and it provides a useful opportunity to rethink the design and interfaces exposed by the code.
Conclusion and recommendations
That brings us to the present day. I can write functional code that is useable by others. I am reasonable handy with the tools of the trade, but I still have a lot to learn about Software Engineering as a discipline. Also, I have never really worked on code as a team, which is necessary for very big projects.
When I look back and despair at ugly code I wrote last week, I remind myself that my main job is turbomachinery aerodynamics; I am not a professional programmer!
I can draw out a few general recommendations from my personal experience:
- Anything computer-adjacent is useful experience. Becoming familiar with abstractions such as files, operating systems, a text editor, and so on is an important precursor step to programming. Getting to the nuts and bolts of computing is harder these days, when many people’s primary computer is a locked-down smartphone.
- Start with small projects. Do not try to make a game as your first project. I needed exposure to many small individual problems throughout my PhD before I really got comfortable with coding. If you are lucky enough to be learning programming for fun, rather than on the job, this Hacker News thread has some recommendations of books structured around small projects.
- Choose one primary language. I think it is good advice for an Engineer, who just wants to get the job done, to specialise in one programming language at an early stage in their career. Twenty years ago, this would have been Fortran, but now you should pick Python. Staying with one language allows your expertise to compound over time; switching from Matlab to Python as I did stunts your progress.
- Do not waste time on proprietary languages. This is related to the last point. One cannot rely on future employers having licenses to your preferred commercial offering. Open-source languages have wider ecosystems with more learning materials and example code available online.
The computing scientist’s main challenge is not to get confused by the complexities of his own making. — E. W. Dijkstra