With 10+ years of being a professional programmer I think you will automatically get the insight of being agnostic is really the only way of surviving and keep your passion for your work.<p>Those who fail at this will stop working as a programmer, move out in the woods, build a cabin and live happy as a farmer.<p>When you're young you will inevitably always have an attraction for styles that ring the most true to you. You have one single hammer you have learnt to use or even worse, have heard really good programmers at Hacker News prefer to use, and therefore you use this hammer for everything.<p>You are a poser.<p>Most programmers have been there. Posers can be extremely sharp and useful if they get to do what they're good at, but they are posers nevertheless and at the start of a humbling journey to agnosticism - getting shit done instead of bickering and posing.
Oh my god, I've been in this situation. We had an executable that had the behavior "if the program is started with '-s', it's a server. Otherwise, it's a client that connects to the server". While the rest of us moved on to the next feature, a Purist came and rewrote that code to use a dependency injection framework ("Guice", in this case).<p>On monday, we said "hey, there's a new bug - when started in Server mode, this code still tries to open a connection upstream! It looks like a Client class is getting created when we don't need it! ... and hey, where did our code go?"<p>The Purist said "oh, well, that's a <i>weird thing to want</i> and isn't supported by the framework, but I can put a kludge in here...." The kludge is, unfortunately, too weird to describe in english - he played a game with lazy loading and well-when-this-happens-then-that-makes-this-happen. And the rest of us had better fights to fight so we just let it stay.
True Agnostics don't write unit tests. In the real world, agnostic code starts with just four lines that hide a single business rule, but balloon into gigantic 600-line methods that everybody is afraid to touch because weird business rules from the Singapore office are hidden away in strange code constructs in the middle someplace.<p>But the agnostic developer doesn't even consider refactoring it, because it "just works".<p>90% of devs are true agnostics, never refactoring, never unit testing, barely above the fizzbuzz level. Raganwald's example isn't an agnostic at all: he's a faithful parishioner, dutifully worshiping at the Church of St. Agile of the Clean Code but wisely avoiding the fanatics and the church politics.
It's unclear to me what the author is criticizing. If the argument is that putting form over function is bad, where form is more important than the thing actually working, then I agree with the post. And that's what happened in the example, the rewrites all turned out not to work. Replacing working code with broken code is never good.<p>If the argument that form is irrelevant as long as everything works, well then I disagree entirely.<p>Just working doesn't mean it's good enough, and really the big reveal in the post is completely beside the point because all those rewrites did not work but that's not what the author is criticizing.<p>Code has to be maintainable, it needs to be readable, and that matters.<p>Also it's pretty much cliché at this point to trot out the straw-man turns everything into a verbose classes programmer. I've never seen anyone turn a simple one line function into 3 files of classes with tons of unnecessary methods ever in my career. In Java you do create classes for a single function sometimes, but that's because you have to.
> <i>Update: Reviewing the comments made elsewhere, I see that this post fell into the Fizzbuzz Trap: By quoting a programming problem—no matter how banal and contrived—the article was bound to provoke a huge amount of dicussion around the correct way to solve the problem, while ignoring the point of the post.</i><p>For his next blog post, maybe Raganwald should write about the pitfalls of using allegories.
I always find it fun to read through these kinds of stories, particularly when we all know we've been there, are going to be there at some point, or are sitting there right now. For instance, myself and two other co-workers were working on a project several years back that involved passing around an array of values. I was building out a Swing based UI layer and infrastructure to wrap their logic and database access layer. At some point, something broke in the backend and I tracked the issue down to a block of code that was taking an array of values and constraining it to have no duplicates. The problem being that our dataset could easily have and should have duplicates. I fixed the issue with the comment "This imposed set semantics on something that is not a set."<p>Granted, I could have written the comment a little better, i.e. more context as to why set semantics were an issue. My co-workers later told me they found my change and spent 3 hours or so trying to figure out if I was right or not, eventually concluding I was. Of course, I was just down the hall and neither I or them thought to go ask the other about why things had changed or if it was a valid change in the first place. A little communication could saved a large amount of time ... Ah well, live and learn.
<i>For no matter how good or bad the Agnostic’s code is, why did the Librarian rewrite the Ascetic’s code?</i><p>Because rewriting code that looks ugly is one of the ways people use to begin to identify with the codebase and make it easier to understand for themselves. The example in the article is clearly made up to show how stupid it it to change things that "just work". Except in reality those things are usually ugly, large and complicated, and <i>eventually need to be changed anyway</i>. The way to deal with them is make people responsible for their own code, so that they add tests or at least some comments that explain why and how things are the way they are. Simply being fearful of change and blaming the last person who touched the codebase for all its problems is not a viable long-term strategy.
What did I learn from this? Srtick to "no ticket -> no change" If you spot a problem write a ticket and discuss the problem.<p>If there really is a problem it will be fixed. If it is not considered a Problem it will be archived and documented as "NOT a problem".<p>And if the 'fix' breaks the system you will not be toasted because hey we talked about this and all(including the boss) agreed we should change this.
> it was only supposed to pre-pend one or two zeros when importing zip codes from CSV files. Anything with fewer than three digits is supposed to be an invalid code<p>The true problem here lies with the poor choice of function name on the part of the original author and lack of commentary. The name "formatted_zip_code" automatically primes people to expect that you passed a (possibly mal-formed) zip code and the function should format it properly. Under that metric, the original code was indeed deficient and should have been changed. Whether it retained the original style is secondary, but there were clear functional deficiencies.<p>"Anything with fewer than three digits is supposed to be an invalid code" is not reflected in the code (why does it return digits and not trigger an error there?) and apparently there are no comments, so it should be no surprise that it needed to be fixed.
So, as a programmer with at least "a lot" of experience (these things are hard to judge, especially among folks here), I'm trying to extract some learning from this.<p>I'm not at all sure about my classification. Probably somewhere between Librarian and Purist, perhaps (but these things feel somewhat harder to apply when most of the code is in C).<p>Should I fight the urge to re-write code that I see as broken, since it's just me wanting to re-shape the code in my image? That's emotionally somewhat difficult (bad code hurts the eyes) but certainly doable, but it also seems a bit counter-productive since I really think there can be some value in sharing experience. It's also, of course (?) hard to really believe that it's all narcissism, to me bad code feels objectively bad. If that even makes sense.<p>I guess it boils down to "co-operation is hard" for me, right now. :)
Here's an amusing code snippet for you:<p><pre><code> >>> (datetime.datetime.now() - datetime.timedelta(days=2007)).__str__()
'2008-05-14 18:32:57.195554'
</code></pre>
Looks like this old goldy originally turned up when hacker news was just a yearling...<p><a href="https://news.ycombinator.com/item?id=188873" rel="nofollow">https://news.ycombinator.com/item?id=188873</a>
I have had the case where I wrote an immutable class and then later someone modified it so that a few of the properies were no longer immutable. That developer then complained that his changes broke a bunch of unit tests and that perhaps we shouldn't write immutable classes because they are too fragile and make it difficult to reason about and use the class in code. I disagreed with his view, but his view was no less true than my own. We had completely conflicting and incompatible coding philosophies.<p>This made me think about something(and perhaps it is a horrible analogy), but how would you play a game of chess if every few moves you had to let someone else make your move for you? Would the strategies that worked and allowed you to win by yourself in the past start to fail with this new constraint? Would you have to abandon certain types of plays or would you spend your time cursing the Gods because the other players couldn't see your strategy nor you theirs (even if you tried to discuss it)? Or perhaps you would realize that the problem is that chess is simply not a game that is meant to be played this way.<p>In the end, we had more success using clearly defined boundaries and trust rather than a collaborative process based on overlapping responsibilities and communication.<p>I'm not making a universal claim, I'm just telling my story.
The whole problem could have been avoided if the agnostic had just added a simple comment stating that the code intentionally only handles strings of 3 or 4 digits.
If you want to treat a number like a string, why not just treat it like a string? When people enter zip codes online they enter 07045, so why not just store that? Even forget about other countries like Canada it seems like the best way...
I like all styles except the one of "Purist" in that example. In that example, the Purist code is way too bloated and boilerplate (if it indeed has tons of subclasses just for such a tiny problem), and I dislike boilerplate.
I've played each role at some point, in several languages. Now, if I modify code written by a Purist for example, I try to use the purist style. I don't compromise on correctness however, which sometimes means commit messages are much longer than the diff itself. It works great if everyone is willing to learn from each other -- when ego doesn't get in the way that is.
I'm currently working on a codebase written by "agnostic". Simply put, it's a terrible buggy mess bordering on industrial sabotage. Whenever I have to touch up some code, I have to rewrite the surroundings to my standards, just to keep my sanity intact.
He left out the Test Driven Obsessive who insisted on writing tests much more devious than the actual input, and the Machine Learner who thought that iterated map/reduce should be able to generate an optimal function based on that input, and probably others.
This post illustrates the folly of valuing form over function. Certainly we should strive to write code that is readable and robust. However, it's important to remember that before all else the code should actually work.<p>The example used as a demonstration is a bit like an episode of Three's Company. The premise revolves around a simple misunderstanding, and it's a bit hard to believe that the misunderstanding went on for so long. The original code is a prime candidate for documentation, and there were even unit tests. If you accept the misunderstanding at face value though, the lesson works well enough.
I'm far too insecure to force my taste in code on others like that. Though I find the Ascetics code utterly unreadable (is that why I still don't grasp Lisp?), and the Purist's code ridiculously over-designed.<p>But still, if someone writes code like that and I'm not the one reviewing it, my instinct is to assume there's a good reason for it to look like that. If I'm reviewing it, you can bet I'll want them to explain why they do it like that, and more importantly: explain what's so wrong about the original code that it needed to be changed.
It seems to me that the core of the problem is that programmers are both writers and critiques.<p>Indeed, imagine a world in which authors would publish in a magazine both their own short stories and a review of a short written by another author for the same magazine.<p>Unlike our authors in this Magazine From Hell we programmers have objective criteria on which we can base our critique of the work of others when we <i>have to</i>, namely a specifications, coding standards, etc.
This really seems like a trivial problem but it's one that has been bothering me ever since. I've found it to be too stupid to even address this, even though I assume that a "non-trivial" amount of development time is wasted because of it.<p>As a perfectionist I'm more often than not engaged in rewriting code "the right way". Generally I've found it hard to accept that there are so many ways of doing things. Worse, some solutions perform better while others are easier to read. Even worse, there's usually a trade-off between those two.<p>To address this I've came up with two ideas. The first would be a language that would be ultra-restrictive, so that there could be only a limited number of ways of doing things. However my guess is that this has been tried and failed.<p>The other idea would involve some clever IDE and/or version control system, that would allow different versions of constructs to co-exist. In other words there would be different "views" on any piece of code. In fact, this is already the case with documentation which can be seen as a "natural language" view on the code.<p>This would solve the "The Narcissism of Small Code Differences"-dilemma, as every programmer could keep his favourite version. But what's more interesting: Based on the assumption that rewriting code for ideological reasons is common behaviour that can't just be stopped, it would be nice (and more efficient) if the rewritten code could at least serve some other purpose. As different versions of a function exist, they could be invoked based on some criteria (probabilistic or as a fallback) with the intent of increasing fault-tolerance.<p>Of course having a fallback is part of the motivation for version control systems, but these are not able to utilize different versions in a systematic way/at runtime, at least not as far as I know.
I was expecting the story to be that even "trivial" problems have complications once I saw it was about zip-codes. :)<p>A few reasons I've seen where assumptions about how zip-codes work lead to not taking money:<p>Ireland - no zip-codes (outside Dublin) - dont do mandatory fields<p>UK - numbers and letters - dont assume only numbers in zip-code<p>Brazil - 9 digit codes - dont assume max of 6 or 8 characters
>Anything with fewer than three digits is supposed to be an invalid code<p>So then why if your code is outputting "57" to the input "57" while the librarian's cleaner and more reasonable solution gives you "00057", why is yours more correct? Shouldn't the function work something like<p>if digits.size<3 raise 'Invalid zip code'<p>digits.rjust(5,'0')
The thing that always bothers me is that programmers throw around their own opinions like its their divine right. As if they are unappreciated artists.<p>Its a craft yes, but really its also a logical profession. Strongly held opinions suck all the joy out of conjuring bits to do your bidding. Can't we all just get along?
Not sure why these are considered different coding styles. Besides the rather contrived examples of arguments regarding the right way to do things, this seems like standard refactoring.
the lesson here is don't refactor known good code.<p>when you do so you introduce a risk of breaking it through lack of understanding.<p>more generally it introduces a potential point of failure when you refactor code. as with all things that introduce the potential for bugs and errors it should only be done if that chance seems less than the chance that it will fix a known issue of greater severity...
If I had a nickel for every time I've heard "Why did you do it that way?"<p>Usually, it's because the simplest solution is the best one.
>“Hey,” he asked, “What happened to that piece of code? It was for zip codes, it was only supposed to pre-pend one or two zeros when importing zip codes from CSV files. Anything with fewer than three digits is supposed to be an invalid code. Empty strings shouldn’t be converted to five zeros, as far as I know.”<p>This is what comments are for, people!
So agnostic code has a bug since doesn't raise an Exception in the else clause?<p>Anyway the others changed the algorithm, interpreting what they believed it should do. That's something unavoidable sometimes due to convoluted code and lack of documentation, but not in this situation.
I think there's a straw man here.<p>I don't think any sensible person from any paradigm would have objected to the code as it was originally written. The substantive arguments between paradigms happen at larger scales of code organization.<p>So it comes across to me that the "agnostic" is trying to make other schools of thought look ridiculous. The trouble is he's done it by cheating.