Hacker News new | past | comments | ask | show | jobs | submit login
The Narcissism of Small Code Differences (2008) (raganwald.com)
170 points by anuragramdasan on Nov 11, 2013 | hide | past | favorite | 126 comments

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.

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.

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.

You are a poser.

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.

You're missing the point. The purpose of this post was to illustrate how silly little differences in coding style cause lots of unnecessary work. The agnostic's version was the best in this illustration because they knew what the requirements were. You can easily adapt the ascetic's, the librarian's, and the purist's version into the "right" code as well.

The point is that we each have our own different style of doing things, and frankly, I'm a bit incensed that you're using this post to hold up your way of doing things as the "one true" way of doing things while calling everyone else a "poser". I think this comment is exactly what the author is trying to say is bad.

I don't know. Greatly influential hackers such as Torvalds, van Rossum, pg, rms, etc. do not seem to illustrate agnosticism very accurately to me.

Sure, but these guys each have the benefit of being King of their Kingdom. Most people writing code have neighbors they need to get along with and cannot rule by fiat.

Not only the benefit, but the responsibility. By mandating a certain "way" and ending the discussion, they essentially nip "the narcissism of small differences" in the bud. As the leader, this is one of the useful things only they can do.

Either you're being sarcastic, or defdac's point has flown completely over your head.

Trying to imitate them does not make you them. It makes you a poser.

How about he's not being sarcastic nor being a poser??

My opinion: there isn't one single good way to do programming. Often, you want your interfaces to be referentially transparent (stateless). Usually, it's best for source code to be in the functional style as well-- but not always. Sometimes, mutable state is exactly what you need. But it shouldn't be the default, unless you're working at a very low level.

There are, on the other hand, a lot of bad ways to do programming: FactoryFactory nonsense, software-as-spec systems, waterfall methodology. We've seen them several times in our careers and have a hair-trigger sensitivity to stupidity, because we've seen it cripple or kill projects.

Great programmers tend to be unforgiving in their condemnation of the bad ways of doing things, but hesitant in accepting one programming model-- even a very good one like Lisp-- as being the One True Way of doing things. As soon as you have a One True Way, some very smart people disagree with you-- and that's a good sign that you're at least partially wrong.

In other words, it's good to be passionate about using the right tools for the job. It's a problem when people think the same tool is right for every job.

It seems that in saying that having a One True Way attitude is wrong, you've developed a Not True Way attitude. Waterfall methodology might work just fine in some situations, software-as-spec works on smaller applications moving to a different platform, and I haven't found one, but I'm sure that the FactoryFactory nonsense can work to keep order in extremely large codebases (I work on a large codebase, and haven't found a need for super abstraction, but that's just me).

Calling these unequivocally bad is counter to the argument you're presenting (which I happen to agree with).

software-as-spec is out of vogue now? I may have misunderstood, but I thought software-as-spec is the same thing as test-driven-development, which I could have sworn is still lauded.

Can you explain why you think they are the same? SW as spec is problematic because whatever the sw does is interpreted as being what it is supposed to do. So how do you identify bugs? At least with TDD the tests serve as some kind of documentation.

I'm no fan of either, generally, but I also don't work on "move fast. break things" type projects.

I'm not a software developer, so I could easily have misunderstood what sw-as-spec is.

I definitely do not work on "move fast break things" projects.

Note: This is a created dilemma, with one horn being a strawman (with namecalling of those who chose that horn), and one horn occupied by the chosen "good position". This is a rhetorical device used to force decisions to the chosen "good position".

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).

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?"

The Purist said "oh, well, that's a weird thing to want 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.

I love the linked article and this post so much. I've seen so many coders come into a new project, think 'ugh, why is this so bad?!' and then refactor it their own favorite way. When things break, it's always the same sentiment as your guy - 'oh that's a weird thing to want', as if function should follow form instead of the other way around.

There can be a lot of value in refactoring code, but you should always refactor to _reduce_ bugs, rather than introduce them, and increase maintainability, rather than obfuscate it.

When I see terrible code and set out to fix it, I try to change it into what the author probably meant, rather than how I would have done it. And usually that's a lot faster, because I don't have to rethink and rewrite everything.

And sometimes the ugly approach really is better. I've introduced tight coupling where loose coupling was causing problems. The components were never going to be used separate from each other anyway, and this removed a whole pile of complexity that we didn't need. The simplest way is often best.

To this guy, and many others like him, I would ask one question: "Is your code shorter?". If not, then I can be quite confident the modification was worse than nothing. (Most of the time, "simpler" and "shorter" mean the same thing.)

When I rewrite code, at least I make sure the result is simpler than whatever I started with. If not, I simply revert back to the the previous version.

well, the thing is, his version replaced all the code with type annotations. So, in some sense, he replaced a 5 line method with no code at all!

Holy crap. This guy still has/had a job?

Yes. He's still with the company. I left for various reasons.

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.

But the agnostic developer doesn't even consider refactoring it, because it "just works".

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.

If the argument that form is irrelevant as long as everything works, well then I disagree entirely.

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.

Code has to be maintainable, it needs to be readable, and that matters.

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.

> It's unclear to me what the author is criticizing.

The author isn't criticizing so much as observing. From TFA:

The post is not about the Agnostic or his code, it’s about the dynamic of programmers eager to rewrite code in their own image, and the hypothesis that our (I am equally guilty of this behaviour) motivation for doing so is to emphasize the small differences between ourselves and others.

Aren't you the author?

I love how people missing the point actually reinforces the point. Well done.

I was the author. Now I'm a guy who wonders if those are my memories, or Tyrell's niece's memories ;-)

I've had the experience of violently agreeing with a comment I found on the web, only to find I wrote it years ago and had forgotten about it. Strange days.

I've had the experience of disagreeing with a comment I found on the web, only to realize that I wrote it years ago and had forgotten about it. I'm not sure if that's better or worse.

...a mournful discovery. Those who agree with you are insane. Those who do not are in power.

1. I think you complicated your point by having the Agnostic write some code without any context as to why it exists and confusing 3 other developers.

2. I think you complicated your point by having the other programmers rewrite it incorrectly.

The more complicated I/we make/made this discussion, the more obvious it seems to me that having programmers who talk to each other is simpler than trying to make up and/or enforce rules to obviate the need for talking to one another.

It almost sounds like you are advocating a style of code ownership where programmers shouldn't make changes to other's code without talking to them.

I realize the context of your article requires a coding example that is somewhat trivial to understand and pick apart by developers who have a "better way", but I also think it's a copout to claim that the programmers in your example are just refactoring code for the fun of it.

A lot of people are rightly fixating on the formatting "feature" of not padding 2-digit zip codes. If I was on that project I could easily see myself wanting to reuse the zip-code formatter and running into an issue where my use case requires the 2-digit zip codes to get padded.

On most projects a zip-code formatter shouldn't be such a big deal that it requires a meeting between programmers. If the original zip-code formatter had bizarro requirements then they should be made more explicit.

For some reason the article avoids the biggest mistake in the whole process. Why did the other programmers make changes without ensuring the test cases passed?

> It almost sounds like you are advocating a style of code ownership where programmers shouldn't make changes to other's code without talking to them.

Sure, we could say there should be more tests, or people should run them. We can say there should be issues filed before you refactor things. We can try to introduce all sorts of processes for avoiding mistakes. And we'd be right.

But it's also nice to talk to a human, even if just casually. I don't want to say "shouldn't X without Y" because then "shouldn't make changes to other's code without talking to them" is much the same thing as "shouldn't make changes without running existing tests" and "shouldn't check changes in without submitting tests that the code fixes."

Shoulds and shouldn'ts should always be secondary to talking.

> It almost sounds like you are advocating a style of code ownership where programmers shouldn't make changes to other's code without talking to them.

Talking to the person who wrote the code you want to change is always a good idea. It's not always possible (because the original coder has left the company or something), but if you have the opportunity, use it. You'll learn something.

I do this all the time. Sometimes the answer is: "You're probably right. Change it." and sometimes it's "It's like that because of good reasons X and Y, and we also use this in another project that needs it to do Z." This understanding is very valuable, and can save you a lot of time.

And the really great thing that happens is when someone says, "Yeah, that code is an ugly hack, and please fix it up, but don't forget good reasons X and Y and Z." Then you get to make it batter without regressing, add the proper tests, and document everything properly, all because you spoke to someone first. :-)

When programmers are working together, like most things in life, it isn't really about the finer details, it is about people trusting each other.

There are layers of trust.

At a fundamental level I trust that my compiler works. I trust that the the standard library is going to work as expected.

At a higher level, up in userland, I trust that libraries or modules that are used in a lot of places or that were written by respected members of the community. If I don't know them I can look at other things, like tests, demos, and documentation.

Only when there is no other information, when you don't know of the person or their work, when you see no tests and no documentation, will you need to actually go in and look at the code.

I take two things away from this.

One is that in organizations where this is happening there are fundamental problems with co-developer trust.

The other is that monolithic code bases where an organization owns the code rather than individual authors owning the code can lead to an atmosphere where no one and everyone feel that they have the say over specific implementation details.

We can design systems and workflows that can implement and support developers trusting one another. If a team is building a system, components could be authored and published under individual author ownership, and then stitched together by the team to build a product.

Of course this is fundamentally a transcendent problem. The issues aren't with the details of the system, the issues are outside of the system and with how the PEOPLE relate to one another.

This is very true. I think it's also probably true that it gets harder to trust code as you get closer to the coalface, much the same way as when collaboratively working on a document, one may become frustrated by another person's grammar or style - yet a third party reading the document would not be able to tell the difference. This I suspect also applies to chefs, plumbers, architects, and many other professions.

> It's unclear to me what the author is criticizing.

I think he is criticizing the desire of people to force their ways of programming on others.

When people are forcing their ways of programming on others becomes a problem it's because of a lack of clear guidelines for the project.

Code shouldn't be littered with half a dozen ways to do the same thing. It reduces maintainability and readability.

Use a consensus or a person in charge to pick a way to do something that can be done many ways, and then always do it that way, and yes force people to program that way for the sake of the project.

Some people, sometimes very skilled and extremely intelligent people, are just entirely process adverse, seeing it as needless as long as everything works. Yes working code is important, but being part of a team, and working with the team is pretty important. Maybe you don't need the process, maybe you can read code like Mozart writes music but others can't. But maybe think of your team sometimes who have to follow in your footsteps.

The author is not criticizing or championing anything. He's just being descriptive. He attaches to his description a reference to the psychological theory that similar people have an innate need to preserve a degree of individuality. This applies to tribes, romantic partners, and software development teams.

While one may or may not agree with Freud, the article is at least food for thought. Presenting software conflicts in this way asks us to acknowledge our instinctual psychological motives and question our claims of objectivity, technical soundness and pure logic. This kind of in-fighting serves only the individual and not the team.

Personally, I think there is great value in having a plurality of ideas and voices, but in a commercial setting, they must ultimately serve the team. This creates tension because individuals must be able to restrain their impulses.

> 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.

For his next blog post, maybe Raganwald should write about the pitfalls of using allegories.

I'd read that, but only if he actually used an allegory to illustrate his point.

Same here. Although i wouldn't read it if he used a self-referential allegory.

You wouldn't be able to because it would rip a hole in the space time continuum.

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."

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 made this same mistake refactoring a function to not process duplicates that the original dev caught in code review. I argue that assumptions (intentionally re-processing duplicates) should be explicitly communicated through comments, unit tests, or assertions.

In the article, author states that existing unit tests checked that two digit zip codes were invalid. The question remains how a refactor that fails unit tests was pushed to master.

For no matter how good or bad the Agnostic’s code is, why did the Librarian rewrite the Ascetic’s code?

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 eventually need to be changed anyway. 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.

I think you missed the point. It isn't to show how stupid it is to change things that already work. It is to show how often the motivation for trivial changes is just to make your code appear sophisticated and stand out.

The point isn't very clear from the article. Frankly, I've never seen anyone changing working code simply for the sake of showing off their sophistication, so it feels like a straw-man argument. What does happen quite often is what I described above: people changing code to make it more comprehensible for themselves. If that happens a lot to your code, the answer isn't felling butt-hurt (which seems to be the case with the author) and blaming it on "dysfunctional" personalities of other coders. The answer is to write comments, do better job naming stuff, be clearer about things in general.

The point is very clear. For example, changing a library written in a functional style to a hierarchy of classes and interfaces might be totally reasonable to someone who lives and breathes OOP. To them, the first style is not comprehensible and therefore "wrong". But does it actually solve any problem?

> * Because rewriting code that looks ugly is one of the ways people use to begin to identify with the codebase [...]

Surely you must see that if more than one person is working on the same codebase that this approach does not scale. If everyone on a 6 person team is constantly rewriting the code so that they each understand it better, and each of them has a different way of understanding code, well, that's going to be quite the clusterfuck.

What did I learn from this? Srtick to "no ticket -> no change" If you spot a problem write a ticket and discuss the problem.

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".

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

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.

"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.

Exactly. The fault lies with the original author in not explaining the intent behind the function, why it is written in the way it is.

I've seen this kind of code time and again from coworkers who have the "code should be self-documenting" mentality, who refuse to put in comments explaining why they wrote a function a particular way. But what they think is self-documenting, very, very, very rarely is to others.

Excellent point. Clear concise code only explains what it does, names and comments should be added to explain why it is needed, how is it being used.

Problem is that specs change and need tweaks afterwards. I wouldn't always think to update a method name, though I am quite liberal with comments. I guess I should start to look out for that as a way of improving my code.

Maybe another component threw the error when zip codes were not 5 digits long.

The whole tale is just "if it ain't broke, don't fix it."

>The whole tale is just "if it ain't broke, don't fix it."

I struggle between this and the accumulation of technical debt. Sure, it's not broken... but that doesn't mean it can't be improved.

This is the correct code:

  def formatted_zip_code(digits)
    case digits.size 
    when 4 then "0#{digits}" 
    when 3 then "00#{digits}" 
    # 2 or less digits is an invalid input
    else digits 

I'd improve that because of this:

-> if the argument doesn't conform to contract, an assert/exception should be thrown so that the caller can recognize the existence of an incorrectly used function.

Are there zip codes that are 2 digits? I'd fall fallacy to this post and once again argue to the contrary. If this function is about formatting zip codes with leading zeros, and zip codes are either 4 or 3 digits. Why would an invalid zip code be passed to this method, a formatter?

Normally I'd agree with your argument about failing to meet a contract, but why would this formatter method get anything but a valid zip code.

The first 3 digits of a zip code represent the sectional center facility code, the smallest of which is 005. One would assume that 00501 is the smallest zip code (which apparently maps to HOLTSVILLE NY )



> why would this formatter method get anything but a valid zip code.

Ask not, lest ye receive an answer.

Seriously speaking, you can't trust that callers will be sane unless you establish a priori with a higher level contract that all possible callers will be sane.

I'd argue that, since the comment regards valid input, the note should appear at the top.

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.

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).

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.

I guess it boils down to "co-operation is hard" for me, right now. :)

The point is, the code wasn't broken. It just wasn't done the "right" way.

I've had to fire people because they could not stop "refactoring" code that wasn't broken. Their commits were half in the code the were supposed to be working on and half scattered, random changes like the changes from the article.

You're not "sharing experience"; you're wasting time, money and energy. You are insulting your co-workers intelligence. You are pissing off your boss.

Worse, your co-workers now have to merge your random edits into their work because they were actually assigned to work on the section of code you "fixed".

There is nothing worse than doing a pull and finding a bunch of small, conflicting and ultimately useless changes on the head, ruining your morning coffee.

Heh. Your comment actually made me blush. :)

I certainly hope I do whatever I do with a bit more finesse than what you imply. So far co-workers do not seem to be offended, and no boss has ever seemed pissed off to me.

From your remark, it sounds as if you consider "refactoring" to be almost a swear word, and something that should never happen. I just can't agree with that, even with code I've written myself I sometimes realize I did it wrong.

I practice commenting on other people's code a lot online, that seems to work pretty well, too. Of course it's even harder to notice if I'm offending people online.

> From your remark, it sounds as if you consider "refactoring" to be almost a swear word, and something that should never happen. I just can't agree with that, even with code I've written myself I sometimes realize I did it wrong.

Ha! Not really, in my career I think I am still "net negative" for lines of code; I once refactored a 100K line medical diagnostic program down to ~10K lines for instance. What is a swear word is people who use "refactoring" as an excuse to start dicking around with stuff that doesn't need to be fixed.

Refactoring certainly has it's place, but needs to have a purpose: new feature set that requires the rework, performance, what have you. Too many developers just pick something that is working and "refactor" it ... then you end up with the same thing you started with, just "better" and with different bugs.

I immediately thought that the Librarian one was sensible and correct, whereas the others seemed like very strained satire. So I'm guessing that makes me a Librarian.

But maybe a cynical, world-weary Librarian like the discworld one, as touching someone else's crappy code without some larger plan in mind seems destined to cause you heartache, following the standard "person who last touched it is to blame" rule.

The librarian's change wasn't so bad, since it was functionally equivalent to the previous one. It was also more legible than the previous one, although she probably should have left it alone.

I think I'm an agnostic. "if it already works[1] then trying to fix it is a bug in the programmer's brain".

(Note: I am a user of many ancient and venerable old FORTRAN libraries, which I have no intention of rewriting because "there is a better way." Yes the glue code is a pain, but you don't need to write it very often.)

[1] or passes the tests, if appropriate.

Many if not most developers have the same feeling, that "bad code" is objectively bad. And how nice it would be if code quality could be graded on a scale. But it really can't - one mans "elegant" is another's "over-engineered". And one mans "simple" is another's "crude".

My perception of "beauty" is the simplest code that could possibly work, with the lowest possible levels of abstraction. I like YAGNI and similar principles. That's a pretty vague formulation though, so while most can probably agree on a high level, the devil is in the details. What exactly is "simple code" for example? Short code? Easy to read? Flexible? All of the above (that'd require Carmack or Thorvalds)? It really is all in the eye of the developer.

I think the takeaway of this excellent article is that whatever your style is, do have respect for differing styles. Don't grade developers or code on what you intuitively feel. Don't assume that everybody else is an idiot and finally don't waste time on debating / enforcing the one true way of doing things.

Cooperation may be hard, but it's also essential. When you see code that hurts your eyes, don't just fix it; discuss it. Set some standards together.

Understand why other people write code like that. If everybody is constantly "fixing" everybody else's code, you're never going to get anything done. Understand the differences and find some common ground. That's the only way to move forward.

> When you see code that hurts your eyes, don't just fix it; discuss it.


Here's an amusing code snippet for you:

    >>> (datetime.datetime.now() - datetime.timedelta(days=2007)).__str__()
    '2008-05-14 18:32:57.195554'
Looks like this old goldy originally turned up when hacker news was just a yearling...



why would you call `__str__` explicitly instead of 1. using str() as you're supposed to or 2. just using print?

Maybe he was in the REPL and found himself at the end of the line, and didn't want to go back to the beginning, so he appended .__str__() instead.

... and Poe's law strikes again.

That's partially true, but note that Python does, to a fairly large extent, have One True Way of formatting code (i.e. PEP-8).

Although, amusingly, I can't actually find this rule in PEP-8.

Not calling dunder methods directly doesn't really need to be in PEP8, they're data model methods for overloading protocols, their whole purpose is to be used by some other "top-level" operation if and when necessary (nb: this may or may not apply when implementing or overloading dunder methods, but that's a special case)

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.

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.

In the end, we had more success using clearly defined boundaries and trust rather than a collaborative process based on overlapping responsibilities and communication.

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.

I really doubt that was the point of the essay. I have seen many a times where different programmer rewrite the entire system and it is not for the lack of proper documentation.

They did. The unit tests covered that piece of functionality. The real issue with the story being why didn't any other the other dev's run the unit tests after making the change?

More fundamentally - why did they change the code in the first place?

Because it was shitty code?

The only thing shitty about it was that it didn't adequately explain the problem it was solving. However, that isn't apparent by looking at the code, the only way we could know that is either by reading/running the tests or asking the original developer. There would be no reason to rewrite until you had a reason.

Except it wasn't.

As far as I see, the behavior of the function is undefined for non-zip strings. There is no 'if digits.size < 3 or digits.size > 5 raise "invalid zip code"' or 'assert(digits.size <= 5 and digits.size >= 3, "invalid zip code")'.

I prefer code to comment in this case. ;)

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...

There are lots of way to accidentally lose a leading zero along the way. For a quick and realistic example make a csv file the looks like:

import it into Excel, edit something and export it as csv again. Unless you pay careful attention to what you're doing, you've just lost your leading zero.

Agreed - if you're going to validate anything at all, define a new type and don't hide the constraints checking within a secret unit testing lair. I hope I would be agnostic about the code here though, there is no point rewriting poorly conceived stringly typed code - that's a symptom that such efforts would be part of a losing battle

The code was for handling zipcodes from a CSV file so we don't really know where the data came from.

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'm pretty sure they were all caricatures. The ascetic's code uses a Y-Combinator, which (as I learned earlier today on HN!) is overkill even if you program in a functional style (basically FP's answer to OO's AbstractSingletonBeanFactoryStrategy). The librarian's code isn't particularly bad, but if you've never heard of that function, you'll have to look it up. This makes the code a little less readable.

I think the point of the article isn't to compare these different styles, but to deliver a bigger message: Style isn't that important, just write code that works.

> ...FP's answer to OO's AbstractSingletonBeanFactoryStrategy

That does not really belong to OO. You would be unlikely to find an AbstractSingletonBeanFactoryStrategy in a Smalltalk project for example (even one under the control of a Purist.) You would find it in a JavaEE project, but even then it is not part the platform, it has more to do with convention that has built up around the platform (and the type of Purists it attracts.)

That's because a Purist amplifies his paradigm, and the form of object orientation he practices in is misguided.

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.

Yes, I think there are two types of agnostics. The ones who can't code and the ones that can. If you know the techniques listed, use them where appropriate. In that example no one technique was better than the other, as the code was too simple to make any of them more relevant.

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.

The obsessive tester may have got it right.

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.

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.

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.

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.

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 have to, 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.

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.

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.

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.

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.

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. :)

A few reasons I've seen where assumptions about how zip-codes work lead to not taking money:

Ireland - no zip-codes (outside Dublin) - dont do mandatory fields

UK - numbers and letters - dont assume only numbers in zip-code

Brazil - 9 digit codes - dont assume max of 6 or 8 characters

I'm always annoyed by forms that make "state" a required field. Yes, my country does have provinces, but they're utterly irrelevant to any kind of address or other info.

I live in Shanghai. China does have provinces, and they show up in your address. Shanghai isn't part of one; it's its own division of equal rank. Amazon requires a "state", though, so I had to have things sent to myself at <street>, Shanghai, Shanghai China.

This is nothing. In Indonesia nearly half of the population do not have a surname. In Algeria many people have "around 19xx" as their birthday, hand written on their passport. So much for mandatory form fields...

It's amazing what a comment and a couple of unit tests could have prevented.

>Anything with fewer than three digits is supposed to be an invalid code

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

if digits.size<3 raise 'Invalid zip code'


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.

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?

> Strongly held opinions suck all the joy out of conjuring bits to do your bidding.

I'd say just the opposite. If you care about programming, you care about doing it the right way. The people who don't have strong opinions are generally the people who don't care, and don't enjoy their jobs, and don't produce good results.

There is a point where having strong opinions results in so much bickering that nothing gets done. Part of doing something the right way is reserving arguments for when they matter.

The solution is to have strong opinions, weakly held.

Hehe, it's not often that you see the comments to an article prove its point so strongly ;-)

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.

when you do so you introduce a risk of breaking it through lack of understanding.

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?"

Usually, it's because the simplest solution is the best one.

Ruby is the perfect language for such a parable given its multi-paradigm nature and community's cultural obsession with aesthetics.

>“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.”

This is what comments are for, people!

Exactly, the proper response should be "Then why the fuck didn't you write that down when you wrote the code?"

People not documenting the code's intention bring down the whole project. Sure, sometimes the code _may be_ right, but without documentation, you can never be sure... It inevitably results in a trust-no-one environment where you can't change a single line safely because it might be doing something else: the weird side effect might be used in some other module, or it's sanitizing data from library X that's no longer used, but nothing is documented anywhere, so you never know.

So agnostic code has a bug since doesn't raise an Exception in the else clause?

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.

sometimes small differences have massive implications. consider: immutability.

tl;dr chaos ensues because weird code was not documented.

Librarian FTW

I think there's a straw man here.

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.

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.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact