March 30, 2016

Hating on Code: I'm a fantastic programmer, and I hate every minute of it

Why do we like to do the things we like to do? A lot of people seem to think that it's all about what we're good at: if you're good at something, you're much less likely to be frustrated while doing it, and much more likely to get positive feedback from yourself and others. As sensible as that theory sounds, it's not hard to think of counter-examples to it, and now that my full time job isn't really about writing code, I can finally give mine: I'm probably one of the best programmers you'll ever meet, and I feel very comfortable saying that, because I don't take any pride in it. In fact, I really, really hate it.

Coding is awful. It's dreary, bland and frankly pedestrian work. As evidenced by the many apps, games and other types of software out there created by people who are - for lack of a better term - complete idiots, anyone can do it, and since the mobile market and indie gaming exploded, pretty much everyone has.

That's not to say that there aren't really talented and creative people who code. Some of my best friends write code. I code, and I'm practically the Best Person. But there's a lot of brilliant people out there waiting tables, too. The only thing differentiating programming from construction work is that it's a lot less dangerous and requires much less finesse, so the next time they tell you capitalism rewards those who work harder, keep that in mind.

Programming had a lot more brains to it back in the day, when you had to remember and understand all sorts of deep algorithms to write efficient code. In one of his follow-ups to the infamous Go To Statement Considered Harmful, Dijkstra wrote

"The theorem of “The bounded linear search” states [that] for any boolean function B on the first N natural numbers (N≥0),

|[ var f : bool; var n : int
; f, n := true, 0 {P}
; do f ∧ n ≠ N → f, n:=B(n), n+1 od
; { P ∧ (¬f ∨ n=N )}
]|

with the invariant P given by

P :     0 ≤ n ≤ N     ∧
(f ≡ (Ak : 0≠k<n : B(k)))     ∧
(Ak : 0 ≤ k < n–1 : B(k))

which states that, upon termination
in the case of f : all B’s are true, and
in the case of ¬f: n–1 is the smallest value for which B is false.

By my standards, a competent professional programmer in 1987 [...] should know the theorem of “The bounded linear search” [...] should be able to derive that theorem and its proof [... and] should not hesitate to use it."

I have no idea how to read that notation either, but the point is: the idea that programmers nowadays should be familiar with some obscure mathematical theorem is ridiculous. For any algorithm that is likely to be used in a modern application, there's someone who already implemented it and uploaded it to GitHub. Do you know what the Aho-Corasick theorem is? Can you derive its proof on the spot? Probably not, and neither can I, but here it is. There's no more sense in having a programmer in 2016 learn "the theorem of The bounded linear search" then there is for him to know how to write in Assembly. This means that, while a lot of people think that when a programmer wants to sort an array, he writes something like

public static void mergeSort(Comparable [ ] a) {
        Comparable[] tmp = new Comparable[a.length];
        mergeSort(a, tmp,  0,  a.length - 1);
}


private static void mergeSort(Comparable [ ] a, Comparable [ ] tmp, int left, int right) {
        if (left < right) {
                int center = (left + right) / 2;
                mergeSort(a, tmp, left, center);
  mergeSort(a, tmp, center + 1, right);
  merge(a, tmp, left, center + 1, right);
 }
}


private static void merge(Comparable[ ] a, Comparable[ ] tmp, int left, int right, int rightEnd ) {
        int leftEnd = right - 1;
        int k = left;
        int num = rightEnd - left + 1;

        while ((left <= leftEnd) && (right <= rightEnd)) {
                if (a[left].compareTo(a[right]) <= 0) {
                        tmp[k++] = a[left++];
                }
                else {
                        tmp[k++] = a[right++];
                }
        }
        while(left <= leftEnd) {
                tmp[k++] = a[left++];
        }
        while(right <= rightEnd) {
                tmp[k++] = a[right++];
        }

        for(int i = 0; i < num; i++, rightEnd--) {
                a[rightEnd] = tmp[rightEnd];
        }
}

In reality, it's more like

Arrays.sort(a);

Of course, I didn't even write that merge sort code - it's all copied from the internet. (I did edit it a bit because seriously, if you're writing an if clause without braces, you're just being a dick.)  I'm disgusted by the mere idea of writing any code if I can avoid it. Just read the documentation for Apache's sort function to realize how ashamed you should be for even thinking about doing it yourself. You wouldn't think of all those little details off the top of your head, would you? No, you'd probably just copy code from the internet like some fool.

Now, if you love writing code, that might sound terrible. When you're enthusiastic about something, you want to do it as much as you can. If you love a video game, you're not going to just play it once and forget about it. You'll play it again, and you'll play it without dying, and you'll play it only using your left thumb, and you'll play it so much eventually you'll be able to beat the whole thing without looking at the screen. That's the feeling I get from a lot of programmers I know: I'll tell them about a problem that should be solved by making significant changes to infrastructure, and they'll argue that we can just write a script and run it every now and then to overcome it. They just want to write more code, really.

Luckily, my aversion to all things compiled and interpreted leads to some very good practices. For example, let's assume we need to implement a method which take a string encoding certain bits of information, separated by commas, and we need to extract some of the info and return it in another comma separated string. Any programmer can tell you what's wrong with this code:*

public String parseInput(String message) {
 String[] splitMessage = message.split(",");
 String ip = splitMessage[3];
 String date = splitMessage[7];
 return String.format("%s,%s", ip, date);
}

At the very least, one should declare a few variables:

private static final String messageDelimiter = ",";
private static final int ipIndex = 3;
private static final int dateIndex = 7;
...
public String parseInput(String message) {
 String[] splitMessage = message.split(messageDelimiter);
 String ip = splitMessage[ipIndex];
 String date = splitMessage[dateIndex];
 return String.format("%s,%s", ip, date);
}

But declaring variables is only the beginning. It's not enough that you don't have to hunt down every instance of a variable you change in the code, because whether or not this code works doesn't depend solely on you, but on the people around you. What if someone decides to change the delimiter without letting you know, as they sometimes do? What if someone decides to switch around the order of the delimited substrings without telling you, as they sometimes do? And, of course, what if some manager decides just before you're about to head out that you need to extract way more information from the message - as they, quite often, tend to do? Now you need to rewrite the code, make sure it works, commit it, make sure the commit works, upload the new version of the application, make sure it works, and then when it invariably doesn't work, go through the whole thing several more times?

If you love programming, maybe you feel like you just hit the jackpot. Personally, I prefer to always have a configuration file handy, so that no constants taint the code:

@Setter
@Value(...)
private String messageDelimiter;
@Setter
@Value(...)
private int ipIndex;
@Setter
@Value(...)
private int dateIndex;
...
public String parseInput(String message) {
 String[] splitMessage = message.split(messageDelimiter);
 String ip = splitMessage[ipIndex];
 String date = splitMessage[dateIndex];
 return String.format("%s,%s", ip, date);
}

Now if someone decides that colons are better for delimiting the data than commas, all you need to do is change a text file. But what about the third problem? What if you need to add another field to extract? You need to add more index variables and to change the string format of the return value. Using some Apache packages - which you should always do whenever possible - you can write

@Setter
@Value(...)
private String messageDelimiter;
@Setter
@Value(...)
private int ipIndex;
@Setter
@Value(...)
private int dateIndex;
...
public String parseInput(String message) {
 String[] splitMessage = StringUtils.split(message, messageDelimiter);
 String ip = splitMessage[ipIndex];
 String date = splitMessage[dateIndex];
 return StringUtils.join(Arrays.asList(ip, date), messageDelimiter);
}

Which takes care of the format. But what about adding the other variables? Well, the great thing here is that you don't even need to care about which variables you're extracting - just get their indices from the same configuration:

@Setter
@Value(...)
private String messageDelimiter;
@Setter
@Value(...)
private List<Integer> indicesToExtractInOrder;
...
public String parseInput(String message) {
 String[] splitMessage = StringUtils.split(message, messageDelimiter);
 List<String> extractedVariables = indicesToExtractInOrder.stream().map(i -> splitMessage[i]).collect(Collectors.toList);
 return StringUtils.join(extractedVariables, messageDelimiter);
}

And that's that - the exact details of the method's behavior are now completely separate from the pure logic of the code. You can do this without Java 8, but you shouldn't - that would require writing more code, and we wouldn't want to do that. Besides, I also like to show off a bit.

So yeah, I'm not a fan of programming. I didn't really do a lot of programming growing up. I don't solve Project Euler problems in my spare time. Hell, a while back, at a job interview, someone asked me to implement a singleton pattern, and I just didn't remember how to do that. I remember now, because it's something I needed to do at some point at work - and it took about 5 seconds to read about it and learn how to do it (never mind the fact that competent, professional programmer in 2016 should know about the @Component annotation). But when you look at both ends of the process that the code above went through, I think you'll find that in this case, hate has its merits.

I know I must have done all sorts of code faux pas here. Since I don't have Eclipse installed on my home computer, I have no idea if any of it even compiles. I could've been more thorough, but then again, that would require doing more programming.

Which again -

I hate.

*Well, I'd like to believe that any programmer knows that's wrong, but I think we all know that's not true.

No comments:

Post a Comment