This is the second of a three part series regarding how to think like a programmer.
In my last blog (How to Think Like a Programmer: Part 1), I discussed how education, particularly a strong background in mathematics, is crucial to being a great progrommer. In this post, I will go over some of the things that I have found extremely helpful in my personal development.
The number one most useful experience in my learning process was EdScheme. Never heard of it? Sounds vaguely familiar? Well, it might sound familiar because of the word "Scheme," a language in the Lisp family. You probably have never heard of it because it is pretty much useless. That is the point of EdScheme. The EdScheme language has only a few built-in functions (less than twenty, if memory serves), and no libraries that I am aware of. Working with the EdScheme books, you slowly build a much more full featured language out of the meager tools you are given. In the process, you are taught a lot about things like recursion, efficiency, and the idea that there is usually more than one way to skin a cat. One example that still sticks out in my mind was building a simple string reversal function. My memory of this is all very hazy, but I seem to remember that we did not even have strings at first; just arrays, chars, integers, and a floating point type. As we progressed through the course, we kept building on our previous projects. Problems and inefficiencies which were not found in the early work were soon uncovered as we progressed through the course.
What this experience taught me was how to break a problem down to its most simple form, an Occam's razor of programmatic thought. Ever since then, I have been much better at looking at a problem and ask myself, "what is the simplest way of making this work?" All too often, programmers find solutions that are incredibly inelegant, simply because it uses methods and tools that they are already familiar with. Programmers should not code for the sake of writing many lines of code, they do not get paid by the subroutine ala Charles Dickens. They get paid to create solutions that meet the why of the users. Anything more than that is simply hubris. Developers are not their own customers. The EdScheme experience showed me that achieving workable, maintainable, and excellent solutions was paramount; the pride of kludging impossible solutions out of inadequate parts never enters the equation.
Another experience which has helped me expand my awareness of good programming is the Perl language. Perl is the most elegant language I have ever used. There is something very satisfying about writing one line of code that does what another language might take five, or ten lines to accomplish. Some time ago, I re-wrote the Perl SoundEx CPAN module as a VB.Net class. The differences in how the code was written were astounding. I was not just that Perl has regex's built in as a string operator and VB.Net requires a regex object. It was the entire attitude of the two languages. Writing in VB.Net is like talking to a four year old; programming in Perl is like talking to an adult. Yes, Perl code can quickly devolve into an unreadable mess, especially by overuse of the implicit operators. And yes, Perl has quite a number of contradictions and inconsistencies. But the Perl language is so incredibly instructive and powerful. In 2000 or so, I wrote a piece of shopping cart software. In 1,609 lines of code (including whitespace, comments, etc.) I was able to pack in a flat file database system (this was before every web host came with MySQL), a full templating system that I consider to be better and more powerful than PHP's (this was before every web host came with PHP), the actual shopping cart system, session handlers (you could not count on the CGI module being installed), error logging, the whole nine yards. It even had a module system for rapidly constructing plugins. Admittedly, it lacked an online administration system; I also believe that it was simply enough to use to not need one. Like EdScheme, I found myself writing my own, custom tailored versions of code in order to handle things which most programmers take for granted, because one design specification was that the only requirements were FTP access and Perl 5.
To get a deeper appreciation for well written code, I highly suggest that you find an open source project that is not a "big player" and try modifying it and customizing it for your own use. Recently, I was working with ZenCart. Out of the box, the software itself is fine. But my customer had extremely demanding needs, and "out of the box" was not nearly enough. I had to go extensively into the code, to the point of modifying its base functionality. I was horrified. The code looked like the worst examples from Computer Science 101. There were nested loops with iterations like "m," "n," and "o." There was little consistency in variable naming. And so on and so on. The code itself was fine on a functional level. But it was so unreadable that sometimes even minor changes would take hours just to find the right place to make the code change. That reinforced in my mind the need to develop a programming style and stick to it. I have a different style for every language I use. But I stay consistent within a language, to the point where I can copy and paste code from one project to another with little (usually no) modification needed. Small things, like always naming my iterators the same thing make a world of difference; someone who sees what a particular variable does in one function immediately knows what it will be doing in a completely separate function in another part of the code, even another program that I wrote. It is especially important for teams of programmers to develop a common style and stick to it.
Finally, I recommend that you get your hands on a variety of different languages, and try writing the same small project in each one. You will quickly learn what works and what does not, for each language. Most programs, at the end of the day, are essentially CRUD mechanisms; get data from somewhere, present it somewhere, allow some changes to be made, and put the changes back into the data source. Some languages are better at this that others. The .Net languages, for example, are outstandingly pleasant to use for straight database-to-screen-and-back functionality. But try to do anything with that data programmatically, and the serious shortcomings of Visual Basic and C# become quite clear. They are weak languages, incapable of doing too much without the giant .Net Framework behind them. Java is about the same. Perl is an incredibly strong language for performing processing, particularly of text, but its systems for getting data to and from the user leaves much to be desired. Its roots as a procedural language make it less than fun to develop user interfaces with it on the backend. And so on. As you learn more languages, you also have the opportunity to see how things can be done. The more tools in your toolbox, the more likely you will be to use the right one for the task.
I know that there is a lot more to be added to this list. Really what I want to make clear is this: always expand your borders, never cease learning, and do not get too attached to any one language or methodology. Learn what works in what situations, and you will be able to meet any challenge.
This is the second of a three part series regarding how to think like a programmer. Part one discussed the reason why mathematics skills are important to the code writing process. In the third part, I will cover the difference between shot term expediency and long term solutions.
Justin James is the Lead Architect for Conigent.