Hacker News new | past | comments | ask | show | jobs | submit login
Follow Up to Crappy Programmer Thread
12 points by edw519 on Nov 24, 2007 | hide | past | favorite | 13 comments
I am adding this thread for 2 reasons: the original thread has become very cumbersome and the debate got out of hand to the point where someone referred to my arguments as "idiocy". This is unfortunate in a forum like this. I come here much like everyone else, to share and learn and take a nice break from time to time.

Perhaps I didn't explain myself well enough in the previous thread. Well, here goes...

I wrote the Fixed Assets module for a division of CSC in 1987. Everything went pretty much as planned (analysis, design, coding, testing, deployment) until the code review. The beta customers were very happy, but my code could not be accepted into the repository because it was in violation of their standards. The minor violations (usually variable names) were easily fixed, but the biggest violator? Early exits. I thought that this was ridiculous and used many of the same arguments people used in the previous thread: less code, cleaner, faster, easier to read, etc., etc., etc.

The response... "You don't understand. We have a backlog of 495 bugs on the bug list. The original cause of about one third of these was the original programmer violating "single entry/single exit".

This was hard for me to wrap my head around. How could cleaner, simpler code be harder to maintain? Because you can't count on those who follow you to be competent. So there are standards that a lot of people much smarter than me came up with to minimize the problem down the road.

I have fixed thousands of bugs since then and my lesson was well learned. About half the bugs I've seen were caused either by poor variable naming or single entry/single exit violations. Sure, ther program worked fine at first, but after many mods, things got lost. Structured programming was meant first and foremost to extend the life of the code by avoiding this.

So I ask any programmer, "How long do you expect what you're writing today to be in production? What changes will be needed in the future? Who will apply them? Will they have enough time to do it right? Will they be competent?"

(Hint: The answer to all of those questions is, "I don't know.")

That's why I offered a few "tips" related to structured programming. I didn't make any of this up. I speak from the experience of cleaning up lots and lots of messes. Your code may look beautiful today, but who knows how others will mangle it down the road.

Do what you want with my input, just like any other. Take it or leave it. Just a little offering from a digital canary in the coal mine.




I'm curious: how many of those single exit/entry bugs were caused because a function mucked with global state?

Again, I haven't seen this problem in the wild, and I have trouble seeing how it could exist. My functions tend to be actual functions - they don't touch state outside of the function (and lexically enclosing scopes, for closures) - and so if a function exits early, it just gives the wrong answer. This is caught immediately by the unit tests; there's no place for bugs to hide and cause problems later.

The one exception is UI-intensive code, which often requires state because users expect their UI to change in response to distant changes. For this, any "hidden" state is clearly documented and included in the tests, just as if it were the return value of the function. All other destructive updates touch the UI, so they're fairly quickly visible when screwed up (and I have individual tests for subwidgets, that test the GUI components in isolation).

I see a lot of the tenets of structured programming as holdovers from when it was okay to mutate variables. It's state that's the true cause of bugs, not single-entry-single-exit.


Good point. I failed to point out that a lot of this history was in technologies where state was often global (COBOL, FORTRAN, BASIC). How times have changed.


We have a backlog of 495 bugs on the bug list. The original cause of about one third of these was [...]

How could they know the cause of bugs that hadn't been fixed yet?


The single entry issue has apparently been resolved; I can't think of a single modern language that allows calling into the middle of a routine. But it's interesting that after so many years there is no general consensus on single exit versus multiple exits. The PMD static analysis tool for Java will (by default) generate a warning for multiple exits http://pmd.sourceforge.net/rules/controversial.html . In Steve McConnell's book "Code Complete, Second Edition" http://www.cc2e.com/ he recommends minimizing exits, but using multiple exits when that improves reliability. Since he has reviewed work by thousands of developers using many languages and styles his recommendations carry significant weight. I understand that Martin Fowler also makes a similar recommendation in his book "Refactoring: Improving the Design of Existing Code" http://martinfowler.com/books.html#refactoring , although I haven't gotten around to reading it yet.

Based on my own experience I do it either way depending on what keeps the code simpler and easier to understand. One common problem with using a single-exit design is that it often forces you to use a mutable variable, or at least separate variable declaration from initialization. For most of the code I write now I force myself to use immutable ("final" in Java) variables in 95%+ of cases and have found this delivers huge benefits in reliability and maintainability. Managing state well is just as important as managing control flow. I haven't seen multiple-exit designs cause any later maintenance defects that a single-exit design would have prevented.

I'm not sure what problems edw519 has seen over the years, but perhaps many of the maintenance defects he saw were caused more by long routines than by multiple exits. If you keep most routines short enough to fit on a single screen that makes it easy for maintenance programmers to follow the flow regardless of the number of exits.


"...keep most routines short enough to fit on a single screen..."

Absolutely.

What invariably seems to happen is that 30 lines of code turns into 300 in a few years. Seems like most enhancements are inserted "black box style" so as to not "mess around with that which we don't understand and don't have enough time to learn".


I very much enjoyed the discussion, I'm sorry if it got heated. In any case, I think single-exit is fine for a corporation that needs to use clearly-defined, easy-to-follow policies. It's a reasonable baseline.

However, there are cases where trying to workaround multiple exits does lead to more errors. A function that returns 'right' 'left' or 'both' is probably better off having 3 returns rather than assigning the result to a local variable and carrying it to the end of the function. You might declare the local as the wrong type, you might forget an 'else' and accidentally overwrite the return value, etc. "No early exits unless your function has no side-effects" is a lot less clear to most programmers.

Of those 495 bugs on the bug list, how many were caused by broken side-effects?


No need for anyone to apologize. We should all have pretty thick skin here.

My only regret: I took a 5 minute break and posted a top 10 list for a few chuckles. I ended up spending 2 hours here and didn't finish my day's work until after midnight. Pretty soon I may need a 12 Step program for news.ycombinator.com.


There's noprocrast. Lifesaver (well, timesaver) for me...


No need for that. I can handle it. Sure, I can. Really, I can.


About half the bugs I've seen were caused either by poor variable naming or single entry/single exit violations

I honestly can't remember seeing a bug caused by either. Sorry. Obviously, experiences vary quite a bit in this respect. From what I've seen, the most common causes of bugs are:

1. Off-by-one errors. 2. Platform incompatibility. 3. Allocation issues (C/C++) 4. Shared state problems. 5. Not releasing/closing resources.

Function returns? Variable names? They just don't figure.


Single entry/single exit refers to ANY process, not just functions. Don't underestimate the hell who can go through with poor variable naming. Case in point:

2 months ago, a client needed significant changes to a major process in their app. (I had 2 weeks.) This process was a BASIC subroutine called by 16 other processes. Now get this: It was 2800 lines of code with one entry and 16 different exits. It was written in 1991 and had been modified 68 times before I came along. Here are a few of the variables: A, AA, AAA, AAAA, B, BB, BBB, BBBB, C, CC, CCC, CCCC, INFO, FORM1, FORM2, FORM3, HOLD.FORM2, OLD.FORM2, ORIG.FORM2, STUFF, DUMMY1, DUMMY2, DUMMY3... You get the idea. I spent 4 days renaming variables before I could start refactoring. There were some variables I never did figure out. I rewrote the program (550 lines) and hit the deadline. I uncovered over 20 undocumented bugs. I wonder what it will look like 5 years from now.

Everything I write new is either Javascript or PHP. Now that my startup is ramping up, soon I won't have to deal with any of this old stuff any more.

(Aside: I wonder how much production code in the world today is over 10 years old. And how much has been modified over 10 times?)


Sounds as if multiple exits were the least of the problems of that code.


Welcome to my life.




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

Search: