Thursday, 1 August 2013

Good Intentions


The biggest waste of time I know.


I'm one of those programmers who spends a lot of his time wondering "how could I do this better/easier/faster/safer/simpler". Often the conclusion is recognising some common patterns across a codebase or refactor functions into more meaningful units, the bread and butter of keeping your code in shape. Quite often however, "this" becomes programming itself, or more accurately, application development. I start out asking how to make a module better, and trace the question back until I'm questioning the overall architecture of applications in general, rather than the particular program I'm working on.

For example, a little project I'm working on at the moment has a data access layer. It's a small file of functions that wrap database calls. There's a certain amount of marshalling and demarshalling, and a few hard coded mappings from query results to types. So naturally, I'm asking "why can't I generate all this code automatically and not even have to think about it, and have something automatically generate typed mappings? I could just give it my types and it should figure out everything else!" And so I spend a few days reading about ORMs, trying them out, debugging exploratory code, getting a buzz from seeing something work "automagically" and then getting battered and bruised by dependencies, versioning issues, performance gotchas that are now opaque to my reason, eventually whimpering back to my simple wrappers, cleaning them up for an hour and moving on. Yeah, yeah, there'll be a few bugs in that module and I'll probably come back to it a few times, but doing it this way is highly likely to be better than downing tools and rearchitecting the entire project to use an ORM. (Ok, I knew about ORMs a long time ago, but it's a typical example).

And that's a real time waster. Probably the biggest waste of time in my entire life.

Don't get me wrong, about 5% of the time, I'll learn about a method or library or framework or approach that really is better. The next project I design, I'll be able to make an informed decision as to whether an ORM is appropriate or not. (Disclaimer: I said "informed" not "good"). Sometimes it's even relevant to the project. Once in a while, it's not a completely foregone conclusion or utterly obvious, so I actually get to make the decision.

But 95% of the time, it leads to nothing, even if I find something interesting, it's not applicable to the current project, or the next one, and the one after that will be running on hexacore smart watches with 6G connectivity so it's probably better to wait until we're starting work on that before doing a review and making decisions about what stack to build it with.

And speaking of stacks, a lot of the irresolvable flamewars are because fanboi A is arguing about how great his language is, and fanboi B is arguing about how great his entire stack is. They're both right, they're just talking about two completely different things. I'll even propose the humbly named "dubhrosa relation of language elegance": the more elegant a language, the less likely it's embedded in a productive stack. Why? Evolution.

A productive stack is one in which everything important to commercial app development is basically possible and reasonably straightforward, in which you can change code in one part of your stack without creating massive explosions of infeasibility in some other part. Usually these stacks have ugly edges. In some cases, they have entire continents of ugliness, like stacks with PHP in them.

Perhaps this ugliness is a kind of a genetic trait that hints at what drove its creation. Take perl, or PHP. They're both ugly. There are pockets of elegance and undoubtedly they are "powerful", but in general, you can tell they grew without any especially well-grounded master design. You can also tell that the people driving the development of these kinds of languages were really focused on making stuff work. I can be fairly confident that perl and PHP will allow me to connect to any major database. There might be a few ugly bits, but I know it's really unlikely that I'll find myself boxed into a corner, like not being able to connect to a major db, or being able to, but ending up rewriting the standard library for doing so because it's such poor performance. I don't have the same expectation with Haskell or OCaml, for instance.

So, in conclusion, if you're building an app these days, you should pick a stack, a popular, well used one, and stick with it for the duration of the project. Learn to love the one you're with. When you encounter overwhelming ugliness, put it on your workflowy list under "There must be a better way - discuss", write the code that makes you cringe, comment it so everyone knows you're aware of how ugly it is so there's no risk to your programmer-tribe status levels, and move on.

Gratuitous blogpost controversial statements that I probably don't really mean exactly 


Let's put some languages in order of ugliness:

Python,PHP, perl, Java, Ruby, C++, C#, F#, Haskell

(yeah, yeah, Python really is way uglier than C++, just not at first glance, it's the beer-goggles language. Its ugliness is hidden under a layer of cakey makeup and whitespace and bitchy bravado. It tells you how much you deserve list comprehensions and before you know it you're in too deep. You're shouting at the screen "if I wanted a glorified dictionary wrapper masquerading as a programming language I could have built my own! a fast one!". It's uglier than PHP in a deep way. PHP sits in the corner of the library sniffling and covered in acne but is honest and friendly and if you ask him out on a date he says "are you sure?" three or four times. Then he brings you to the fairground and you have a great time up until you fall off the rollercoaster (did he get too excited and push against the guardrail? you'll never know for sure) and break your arms and wake up in hospital and somehow you've caught his acne and his cold but he's there beside your bed with video games and taytos. Python, by contrast, roofies you with syntax and when you see through it, tells you that you're inadequate if you don't understand how great it is, and when you finally leave, he spams your twitwall for months.)

And yes, Haskell is the most elegant, by about a billion miles, don't even try to argue with me on this one. Haskell's community is fantastic, but its ecosystem sucks. Configuration management can be tricky (google cabal hell), and it doesn't fit well in any of the widespread stacks. I've tried using Haskell in a web app, first using the Haskell webapp frameworks, and then just as a CGI within Apache/postgres, pausing only to build a custom json protocol with session oriented connections and a custom client. Oh how I wept. It was like finding the partner of your dreams, perfect in every way, except they wake up and stab you in the back of your head at 3 in the morning on a regular basis. I say this in part because it's sufficiently vague that the absolutely lovely and helpful Haskell guys don't implode in exasperation because I tried version 0.1.3.2.3 of yesod and the random stabbings were fixed in 0.1.3.2.5 (and I'd explain I couldn't use that because I was using a version of the bystring library hand picked to be compatible with a particular version of the vector library that meant I couldn't upgrade to that version of yesod even if I wanted to, oh wait that's fixed now but I need to build ghc from source you say? How about the x64 bug?), and in part because it's entirely true.

There's also the little discussed, but very important, matter of a project's "moron impact factor" that you need to consider when understanding the successful stacks. Think of it this way: every successful project basically never completes, it's a successful application so people are using it, asking for new features, and burdening the system with ever increased load. At some point, you'll have new programmers, old ones might leave, or you're adding because there's just more work to be done. The law of large numbers and cretinous recruitment agents means that at some point some very stupid programmers will work on your application. The amount of damage they can do in a given period (moron detection time) is a characteristic of the stack you're using. This is the hidden value of verbose languages - it takes stupid programmers so long to wade through all the verbose code, that they can't do as much damage, there simply aren't enough hours for them to get to it. What's more, it's easier to make your peace with adding a mediocre programmer to the team to add some feature, because the stack is pretty ugly anyway, so we're all acclimatised to the necessity of ugliness, so we can tolerate ugly but functional code written by a mediocre programmer who was drafted in.

Please let me know when my Haskell stack is ready. Until then I'll be in a darkened office rubbing my temples and sighing.


2 comments:

  1. How long have you worked with perl5? If you want to bash CPAN, by all means be my guest :) But perl is not ugly. In fact it's "as" ugly as Haskell since both revolve around idiomatic coding philosophies, making them less verbose than... Java?
    And you wouldn't believe the morons that work with Java. Almost as much as PHP.

    Anyway I agree with your core message. I look forward to the day I can work with Haskell on a professional level and when the day comes, it will have a more mature tooling ecosystem.

    ReplyDelete
  2. You know, you're right, on second thought I'd put perl way ahead of Java, (maybe between c# and f#?) I'd certainly rather be forced to work on a perl project than a Java one. I once inherited a perl program written by a real master and it was extremely clean and elegant, it solved the problem at hand with a tiny amount of code, and then we bolted on parallelization with almost no effort.

    I concur with your point on Java, recently I had no choice but to work with Java (Android app). I was prepared for poor documentation, but what struck me most was the piss-poor quality of posts on Q+A forums, even Stackoverflow.



    ReplyDelete