65 comments

[ 3.4 ms ] story [ 132 ms ] thread
Oh, well I'm not crappy then. Neither I'm a good one though...
I think it should have been "Signs you are a day coder (and don't know it)"

I am amazed how many programmers I have worked with who are not up to date and just do their job and are not interested in it.

"...and it often simplifies the code to have multiple returns."

Today. And what happens after 10 other programmers modify your slick little multi-return function? Nobody will ever be able to modify it again because the entries and exits are lost among the garbage. This is the ongoing debate I've had for years with "clever programmers". They're missing one critical point: the guidelines of structured programming are not meant to make your life easier; they're meant to keep the code alive longer by making life easier for the poor schmuck who has maintain it (usually me).

Another Sign You're a Crappy Programmer:

You make your own rules in direct violation of effective established principles because you think you know better.

Early returns make code more readable. I'd rather detect an error and bail then have a slew of nested ifs.

It isn't always about being clever.

"I'd rather detect an error and bail then have a slew of nested ifs."

And here's what inevitably happens when you do that:

14 mods are applied and your early exit(s) get lost in the muck.

Programmer 15 puts his mod ON THE WRONG SIDE of your early exit because he never saw it. He doesn't do regression testing, and the data base gets screwed up over the next 6 months.

Then I come along, find the errors, fix the data base, advise the user of the effects of the damage, and rewrite the whole program with single entry/single exit processing so that this never happens again. And then post to a board like this only to re-engage in the same old debate.

Aside from poorly named variables, violation of "single entry/single exit" is the biggest lifespan reducing problem of code bases I've ever seen.

This is the future speaking. Please don't do that.

I see where you are coming from, but I'm just not buying it. First, 15 different programmers are rarely touching the same piece of code over any short or medium span of time. Second, even over the long term, if you have programmers who don't read the code they are making changes to before they change it, you should fire them.

You can make the same argument that a bunch of nested ifs is going to end up with a lazy programmer who doesn't know what conditions need to be met, so he puts it in the wrong place, and now all of your careful code is broken. Coding style is ultimately pretty personal, and this is no different.

"15 different programmers are rarely touching the same piece of code over any short or medium span of time"

I see it all the time. But then again, maybe I'm like the blind man with the elephant; I only know what I've encountered.

Wouldn't it be great if analysis was conducted properly, design specs were tight, functional specs were accurate, projects were managed properly, programmers were trained, resources were adequate, and the people paying the bills had a clue as to what they needed? I've never seen it. That may be why code gets modified so many times. I've even seen "quick & dirty" temporary programs still in production for 15 years. This is the world we have to deal with. That's why we adhere to structured programming principles: to save ourselves a whole lot of trouble down the road.

Questions I have would be:

Why is the function being modified 14 times? It sounds like the original abstraction was poorly conceived. I would hope that at some point before edit #15, someone would think to break the function down into smaller pieces.

"Programmer 15 puts his mod ON THE WRONG SIDE of your early exit because he never saw it. He doesn't do regression testing, and the data base gets screwed up over the next 6 months."

I can certainly see how this is true in a large organization which hires lots of mediocre programmers. However, if you're in a company where the other programmers are top notch, this sort of thing is not a problem.

In fact, if I ever see nested ifs and such in a function where I work, it gets changed to the least number of lines of code possible. I can do this because there is only 1 other programmer here, and I know he's not going to screw it up.

I don't know your situation, obviously, but my initial reaction is... "Holy crap man... run!"

"if you're in a company where the other programmers are top notch"

I've never seen such a thing (and I've seen alot).

Reason #127 for starting your own.

reason #1 to get a new job. You might have seen a "lot", but you've seen a lot of the wrong thing.
lol - I've often wondered about that. I once remarked to a partner, "Why do we see so much crap?" He responded, "Cause the people who know what they're doing have no reason to call us."
That's why "return" (and "throw" and "assert") is syntax-highlighted in most editors. ;-)

Honestly, I've never had a case where I visually missed an early exit. Most of the time, such exits are guard statements, designed to bail out with an exception if some precondition isn't met. Most of the rest, they're "found it, don't need to do any more processing" and apparent from the logic. I'll usually mark the latter with a quick comment anyway, just to be sure I don't miss them.

And early returns let you apply additional invariants to your code. You've checked your preconditions and bailed if necessary; you don't need to worry that this array might be null. You've handled the 1-item base case; you don't need to worry about your recursive function blowing up. I find that broken invariants cause many, many more bugs than missing an exit and not executing code.

"I've never had a case where I visually missed an early exit."

Too bad the same's not true for those I have followed. And when it does happen, it makes the worst kind of mess - the kind you don't discover until 6 months later.

Guidelines and rules are not meant for the best of us; we don't need them as much. They're meant for the mediocre masses who are everywhere and wouldn't know any better.

If your programmers were a) competent or b) wrote tests this wouldnt happen. If they're not competent and don't write tests they should be fired.
Agreed.

Cue the blame-the-programmer-for-not-being-smart-enough comments.

Guys. The goal isn't to provide the "perfect" code that somehow those poor average schmucks just can't understand. The goal is to provide the dumbest, easiest-to-follow narrative of how the computer is going to solve the problem for the user so that the next guy is able to understand it.

The goal is to conceptualize the problem in such simple terms that even a machine could solve it. Then do so.
Ah. Didn't read this when I replied earlier: http://news.ycombinator.com/item?id=83107

You make sense. However, I would just try to find more interesting work than cleaning up after others and then trying to get them to change in a certain way :)

That's what the other rule is for, to not make functions span too many lines ;-)
Don't worry so much about how you or others will modify it, and how it may look when it's 10 times hairier. That's what refactoring is for.
The number one sign you're a crappy programmer:

1. You foist idiocy upon others and refuse to listen when they point it out as such, because someone told you that the idiocy was "effective established principles".

Your "effective established principles" seem to be neither effective, nor established, nor principled.

"neither effective, nor established, nor principled"

http://en.wikipedia.org/wiki/Structured_programming

Isaac Newton "stood upon the shoulders of giants." Interesting to see that you know better.

-----------------------

"...someone told you..."

Actually, I learned what I'm sharing in this thread (and much, much more) from MANY excellent mentors. I hope you find some, too.

----------------------

"The number one sign you're a crappy programmer"

I have hundreds of (well paying) clients that would disagree with you.

----------------------

I "foist" standards only upon those who work for me. You won't ever have to worry about that.

I'll give you the vote because very often hard-set rules in programming are not good.
10 Real Signs You're a Crappy Programmer:

10. The exact same code is in multiple places because you didn't bother to put in into a common function.

9. You have error codes in functions, but never bother to look at them (a sure sign that testing was never finished).

8. You have early exits from loops because you don't know how to properly code a recursion.

7. You execute too much code because you don't know the difference between "if" and "case".

6. You use 10 lines of code when 1 line would do because you don't know any better.

5. You need to run batch jobs from time to time to clean up your data base because you don't know how to debug well enough to find what's messing it up.

4. A third grader doesn't know what your variable names mean.

3. You think "Third Normal Form" is paperwork you need to fill out for one of your 8 bosses.

2. No programmer who has to maintain your code would ever want to work with you.

1. Any programmer that has to maintain your code would be more than happy to accept the consequences of first degree murder.

0. You refuse to use recursion even when working with a language that supports it properly because you think it's horribly slow.
"a language that supports it properly..."

Meaning what exactly? A language that automatically optimized for tail recursion? Are there languages which don't support recursion "properly?"

A few years ago my friend wrote up a few recursive functions in C and they were slower than using a for loop. Since then I haven't used any sort of recursion in C or C++. Maybe my fears are unfounded but I try and avoid those two languages in any case.

edit: I just realized what I actually mean. Some programming languages, or the culture/community around them, do not seem to encourage certain things that can be done with the language. In the case of C/C++, while/do/for loops are used before recursion. In the case of Scheme, you try a recursive solution first and then go for a do-loop if you really need to...or something like that.

> they were slower than using a for loop. Since then I haven't used any sort of recursion in C or C++.

That's premature optimization.

"8. You have early exits from loops because you don't know how to properly code a recursion."

Wow. Not every problem calls for recursion. If you really hate early exits from loops, maybe you should just code the loop's condition differently.

"If you really hate early exits from loops, maybe you should just code the loop's condition differently."

Exactly.

Like I mentioned before, I'm the poor schmuck that has to clean it up because someone didn't bother to take your (common sense) advice years ago.

Sometimes it makes more sense to bail early than it would to restructure. It could be that its more readable, or a more natural progression of things. Take this paper:

http://www.csd.uwo.ca/~yuri/Papers/pami04.pdf

It's really great, for the record. Anyway, the algorithm they wrote uses an early exit. In fact, they use a while(true), the "worst case" example.

    initialize: S = {s}, T = {t}, A = {s, t}, O = {(Empty Set)} 
    while true 
        grow S or T to find an augmenting path P from s to t 
        if P = {(Empty Set)} terminate 
        augment on P 
        adopt orphans 
    end while 
In programming this algorithm, sure, I could probably move the first grow before the loop, and grow at the end. But, now its harder to understand, and violates the afore mentioned copy paste rule.

My point is this, take any such "rule" with a grain of salt. There are plenty of examples where it makes sense to do something atypical.

"violates the afore mentioned copy paste rule"

Put it in a common function. Sure, it may look a little frazzled today, but generations of future hackers will thank you.

You're still making two function calls when you could be making one. If one of them gets lost in the fray, you'll spend just as much time solving that problem.
Are the costs of function call overhead worth worrying about?
Maybe this is the crux of Dijkstra's original arguments.

Which is more expensive, a few milliseconds of machine time a few million times or a whole bunch of human time once or twice?

Not at all. What I meant was, you're calling the function in two places, instead of in one place (inside the loop). You've pasted that function call. Thus, by all the same logic, you are introducing a possible future headache.
One very useful idiom:

while(readLine()) { process lines... }

where readLine() encapsulates the repetition and returns eof as well.

where is the line (to be processed) stored?
In my comment I assumed it's a line in a file and that the termination condition is eof, but you can generalize to any aggregate of items. I'm just combining the test with the increment step so we can use a simple while loop without having to peel the 'first increment' step out of the loop. If that makes any sense :)
Thanks.

I was trying to point out that the idiom becomes while(line=readline()) which I'd rather avoid since = and == often get confused in a conditional.

It would normally be silly to get into this level of detail but I use this so often.

Oh, good point. I am tolerant of globals :). Also of assignment instead of equality in conditionals (with an extra layer of parens to flag their unconventional nature). I've also used class methods where the implicit variable is in this.
(6) Happens even when you know better.

(10) Good programmers don't repeat themselves, true, and to continually improve as a programmer it is important to patiently do everything correctly the first time. However, obsessing over a few bits of code duplication is often a complete waste of time.

"...obsessing over a few bits of code duplication is often a complete waste of time."

For you, maybe. But what about the guy who applies enhancements 3 years later and has trouble getting through testing because the exact same code was somewhere else and difficult to find because of the 67 mods since then.

Minutes "obsessing" today usually saves hours testing tomorrow.

"Minutes "obsessing" today usually saves hours testing tomorrow."

True, but the app has to make it to tomorrow for testing to matter at all. It's better to be in the championship trying to figure out how to manage a tired team, than it is to conserve energy during the semifinals and wind up sitting in the bleachers.

Where'd you ever get the idea that there is any trade-off between "quick" and "right"?

Doing things according to effective established principles saves a little time today and a lot of time tomorrow.

Quick and dirty usually ends up being neither.

Where'd you ever get the idea that there is any trade-off between "quick" and "right"?

I don't have that idea. There is sometimes, though, a tradeoff between current needs and future needs; and that tradeoff can not always be predicted. When that happens, it's logical to give current demands priority.

I can't for example, predict whether a future maintainer of my code would prefer code written with multiple returns, or code that sets temporary values, or code that uses a set of nested ifs, or code that assigns function pointers to a matrix and calls code based on the indices. Each technique has potential drawbackss in terms of future maintainability, likelihood of errors, and speed of immediate implementation. Trying to guess what a future coder (other than myself) will think is just not important in that case.

Effective Principles can only be Established based on a given development environment, and it's the responsibility of those in charge of the environment to make sure the established principles are effective and followed by the developers.

If it were really true that "effective established principles" save time on both the front and the back end, then everyone would be doing them.

The fact is, there's almost always a trade-off between "quick" and "right". That's why so many people choose the former, to the detriment of the latter.

I usually follow the childhood adage "First time it's funny, second time it's silly, third time's a spanking." Probably better summed up as http://www.c2.com/cgi/wiki?ThreeStrikesAndYouRefactor. Problem is, after 2 repetitions you often don't know what the real abstraction behind the duplicated code is, and so you end up with convoluted objects or closures to swap out parts of behavior, while there's a more natural abstraction just around the corner. If you mercilessly refactor after only the first duplication, you might not see it after the second, because the original code has been abstracted enough that it doesn't look like the third repetition.

This also squares with a lot of IBM's experience in The Mythical Man Month, i.e. code needs at least three clients to really be reusable.

"obsessing over a few bits of code duplication..."

Applying good coupling and cohesion principles is not obsessing. It's as much legitimate programming as writing an equality test.

It is obsessing if it detracts from the end product. I'm not saying DRY isn't good practice or that chunks of code duplication are just fine and dandy. However, bits of code duplication are hardly a viable litmus test for judging quality of a programmer. When an actor screws up a line in a performance, he can't afford to stop and correct it. In fact, if he stops to correct a meaningless line he'll cause far more damage than if he'd just kept going. Programming is not always so time-and-performance-sensitive, but it's not time-insensitive either.

Perfect programmers may manage to avoid duplicating code on the first try. But unless we have dozens of Mozarts posting here I'd guess that no one qualifies. That means we make mistakes, and the question of whether correcting those mistakes is good practice or obsession depends on the circumstances.

I'm trying to find some point of agreement with you.

I think from your comment, we can agree that there are two scenarios to consider: when the programmer is in control of the product and when the schedule is in control of the product. "Programming is not always so time-and-performance-sensitive, but it's not time-insensitive either."

Here's the deal: when you stop your design process and kick the compiler up and start coding, you haven't stopped your design work at all, you've simply switched from top-down to bottom-up design. Every line of code you write is a validation/denial of the assumptions and planning you're supposed to do before you start. If you're not refactoring aggressively as you go along, you're not taking the chance to see where your code is supposed to be going: you're simply trying as hard as you can to get to point B from point A. It's like driving a car from one side of the field to the other by letting go of the steering wheel and hoping it gets there okay. Most of the time you make it to the other side just fine, but you have zero control over where on the other side you're going to end up.

To me, that's hacking, and I mean in a bad way. I've been known to hack like a freaking electric weed-eater, trust me! But that's when something else is out of whack on the project. It's a sign that there are serious problems. When you're doing it right, your code is constantly evolving as you refactor to the point where changes become less and less of an issue. So good programming means you spend all the time necessary to refactor with the knowledge that you'll going to make it back in spades later on.

It's like when playing rummy, and you decide to pick up a huge stack of cards. At first, it seems like a crazy move, but if you know how to play the game, you realize that the only way to win is to constantly redefine the problem domain of what you have in your hand. Hacking doesn't get you that -- it just gets you to point B no matter what. It may not be the sign of poor programmer, but it's definitely a red light that something is out of whack somewhere.

My opinion only. Your mileage may vary. Do not attempt to drive heavy machinery after reading my comments. Consult a physician before beginning a regimen of refactoring. Etc.

n a perfect world avoiding code duplication would be a simple matter. Unfortunately people have to make decisions with what they've got and there isn't always a clear right answer.

A simple dilemma:

You have to perform an operation in C that accepts up to 5 parameters and will be executed when a given 50 input sets are found (out of thousands of possibilities).

You can write one function that accepts variable parameters and paste it into the code for each of those 50 conditions. (Assuming you know how to do that in C to begin with -- I don't, actually)

Or you can write 5 different functions for each possible number of parameters, with some duplication in each function. Or you can write one function with 5 parameters and just pass 'NULL' in the unused slots and test for it inside the function.

Or you can come up a few meaningful sub-categories and write 15 different functions that also handle some processing that would otherwise happen further down the road.

Or you can write 50 different functions that correspond to each input condition, that do mostly the same thing at this point, but in the future if you needed to change any particular input set side-effects will be limited.

How do you answer this question without being able to see the future? (And, is it even worth worrying about, when any of the answers will work?)

I have to disagree with #6. You can either write code for the compiler or the human. I prefer to write it for the human, because a lot of times the compiler understands exactly what's going on, but I'm pretty clueless when I come back after only a few days. I think you meant not understanding how the language works so using a lot of extra commands when one would do. However it's common to "dumb down" your code, especially if it is tricky, for the poor schmuck that has to maintain it. Remember -- writing code should not be like writing a mystery novel.
"You are adamantly opposed to function/methods over 20 lines of code."

There are (at least) 2 problems with long funtions/methods: 1) They typically lead to more duplicated (and less reusable) code, e.g. repeated blocks of code between functions that could have been replaced with to-the-point short functions. A typical sign of where to extract a function is when you see a long block with a comment above it inside an even longer function, then replace the block with a function with a name inspired by the comment.

2) The methods becomes harder to test, and possibly even more important, the test code becomes harder to maintain (and untested code of some complexity usually doesn't work), e.g. what happens to your existing tests if you add some new conditions at the top of a long method?

A good argument might be chunking: the (average) human brain can keep only about six or seven chunks of information in short term memory.

A method with 20 lines of codes is roughly 20 chunks. Move some parts of those 20 lines into other functions, and you have created more effective chunks. Presumably, the brain has a much easier time understanding things it can actually keep in memory.

One idea is to create functions instead of comments, that is instead of writing "compute the rank" followed by some code, create and call a method computeRank().

I expected this list to be written by a crappy programmer who didn't know it... but it turns out it isn't.

I'll add two items to this list:

1. You believe anyone who doesn't write unit tests is a crappy programmer. (I'm done trying to explain this to carppy programmers- not only are they crappy they think that unit tests make them not crappy, and so they can't concieve of the reason unit tests are bad (and often they are...) If you want to write unit tests for yourself, that's fine, but if you've ever told someone else that they should write unit tests, you're a crappy programmer.

2. You believe in Scrum. Like global warming, Scrum is a religion... back in the old days, when programmers had to actually program, we used to use the "daily status meeting" as an example of the height of absurdity that pointy-haired-bosses could achieve. Now crappy programmers think that it makes them less crappy. (No, it doesn't, it actually means your code is crappy... especially if you think scum is an improvement.)

Really.

I know I am speaking crazy talk-- at least most programmers under 30 seem to think its crazy talk-- but it isn't.

The reason we have crappy programmers now? I think its cause they are going to colleges and getting CS degrees. Back in the "old days" when you had to write code to become a programmer, when you had to learn and you cared about getting things done well, we had to self teach and the learning curve was higher.... nowadays really crappy programmers train crappy programmers by the ten-score at universities who's primary goal is separating fools from their money (and I mean any university other than maybe caltech and MIT this applies to.) and they just teach really crappy programming practics. Those that can do, you know.

Speaking of which, you gotta be poor at math to go to univeristy in the first place-- teach yourself and start working right out of high school, after 4 years you're senior software developer, you're much better than the people who have been taught bad practices at university, and instead of being up to your armpits in debt, you'll have a good job and a great income... and four years later- a8 years into your career and 4 years after your friends got out of college, they might have paid off their student loans but you'll still be making twice what they were.

But then, I'm expecting to get pounced on because this is a site full of university grads. :-) Sorry, this has been my experience, and I've interviewed a LOT of programmers in my time.

In fact, I've found that PhDs in CS are useless, Masters in MS are good for grunt work, if you watch them close but generally shouldnt' be hired, and people with jsut a BS degree can usually be retrained if you can get them focused... if they get a job at a big corp right out of school, though, they are lost forever. (Most of them anyway.)

All the really brilliant programmers I've known didn't go to college. (BTW, I did, so I know first hand it was a waste of time.)

Anyway.....here's a bonus "how to know you're a crappy programmer":

You're a crappy programmer if you think while (foo) { stuff }

is wrong because the curly brace goes on the same line as the while. Generally ,the sloppy programmers who think they are really productive but produce nothing but shite are adamant about that... the ones who think about what they are writing and are useful, put the brace on the next line.

Really should use that as an interview question.

How many of your bosses did not go to college?
I think it's about time somebody stood up for the "curly braces stand alone" school.

I mean, we can debate silly stuff all day, like LISP versus C, Firefox vs. IE, Mac vs. PC, web 1.0 vs web 2.0, etc. But these "braces on the same line" guys are just completely out of their minds. :) <-- Smiley face means humor was used in this comment.

I've seen whole projects fail over that discussion.

Maybe it really should be used as an interview question. There is no point in mixing same-liners and new-liners together.

Only remedy might be to use a programming language without curly braces.

I have to admit it's a pet peeve for me -- even more than choice of languages, oddly enough. When scanning code, I just like the ability to see my control blocks clearly. Simply because you can do something like this

 if (a==b) { doStuff(); } else doOtherStuff();

Doesn't mean you should.

I would consider this a little bit of a hangup on my part, though, as it seems other programmers go the other way. An interesting topic for a book is all the weird things programmers get hung up about. Variable naming comes to mind, as does error handling strategies. And when we get hung up, usually we're not too shy about explaining why everybody else is wrong!

I would not put everything on the same line, but I admit to putting the opening bracket on the same line. In fact, I consider the other variant extremely ugly. Just consider

  if(...)
  {
    ...
  }
  else
  {
  ... 
  }
vs

  if(...) {
    ...
  } else {
    ...
  }
I can't for the life of me understand why someone would want to move the bracket to the new line ;-) It's just empty (wasted) space without any meaning. I can see the control blocks with the tighter version just fine, plus, it's the Sun Coding Convention for Java. But it's really a fruitless discussion...
I on the other hand find this quite elegant

(...) ? $doThis : $doThat;

Hate to break it to you, but you are just like all the others: every programmer thinks he is the only good programmer and all the others are crap.
You know that you are a lousy programmer (and also lousy at doing other things) if you start to make rules although smart people know:

- rules are for people who cannot think

- every rule has a context where it fits perfectly, but

- every rule has many contexts where it doesn't fit at all

Rules only reflect your current knowledge and your current working situation. This is subject to change.