Bad Programming Habits We Secretly Love (infoworld.com)
snydeq writes: Breaking the rules can bring a little thrill — and sometimes produce better, more efficient code. From the article: 'The rules are more often guidelines or stylistic suggestions, not hard-and-fast rules that must be obeyed or code death will follow. Sure, your code might be ridiculed, possibly even publicly, but the fact that you're bucking conventions adds a little bit of the thrill to subverting, even inadvertently, what amounts more often than not to the social mores of pleasant code. To make matters more complex, sometimes it's better to break the rules. (Shhhh!) The code comes out cleaner. It may even be faster and simpler.' What bad programming habits can't you (or won't you) break?
If you think it's nice to break habits or do unconventional code just for the thrill of it, remember how fun it is when you need to work on someone else's code.
If the code comes out cleaner, you didn't break any of my rules. The rule "make the code as clean as possible" trumps all other rules.
Sure, you can trade maintainability for efficiency and reliability. People do it all the time. You just have to understand the costs involved. If the efficient code gains you a million dollars in performance, maybe you can afford for it to be crappy code. Or maybe you'll be running the code for 10 years, and if it costs you $250,000 to keep a crusty old engineer on staff who can maintain it, suddenly that million dollars in performance may not be worth it.
<disclaimer>I am a crusty old engineer.</disclaimer>
John
We ALL fully document our code, have clear specifications before we write code, use meaningful variable names and rely on IDEs...amirite?
In my case, I only program (after doing it for pay for 45+ years) for myself, and I'm creating new stuff all the time, based on experience. For example, my backup strategy. It started out as a simple script to launch Drive Snapshot. It evolved, into having multiple, cascaded backups on one partition of the computer, which are replicated automatically to my main "server" in case the computer dies. Each computer in the office uses the same central repository. It's got bells and whistles that make my job a lot easier when I experiment...if I try some new app and it trashed Windows, I just roll back to last night's backup. (I believe in 100% backups of all computers...including the server...every night, and schedule "fixit/improve it" time first thing in the morning, so I can rollback and lose nothing.)
So, personally, I now break all the rules, and let my needs dictate the code I write. It isn't specified, it's an evolving organism in my small environment.
Oh, and I'm doing all that in cmd files...might consider upgrading to something exotic, like AutoIt, someday, but I've been saying that for four years now...
Sometimes I will copy and paste a function and just do some minor tweaks were I could have just added a parameter.
Why do I do this? Readability. Having a function called SplitPersonName(string name) and another one called SplitCompanyName(string name) So when I run the function it will be easily readable, as well if there is a bug in one of the fuctions but it works fine for the other. I can just change that one function without having to unit test other parts that could have been effected.
Also I avoid too much Classes that are extended from other classes, that tends to add confusion on where a particular code is being called if you are debugging it from the middle of the class structure.
It is OK to break rules, but you should have a good reason to do so. Also you should feel free to not break the rules when you do not have a good reason to do so.
If something is so important that you feel the need to post it on the internet... It probably isn't that important.
From TFA:
my friend wired together an Eliza-like AI to his editor, and voilà, every function had a few lines of "documentation." The boss wasn't smart enough to understand that the lines meant nothing, so my friend was off the hook. His code was officially documented. I think he even got a promotion!
He should have been shot.
long methods - someone thought it a good idea to limit every method to no more than 20 lines. I think this is a terrible idea, and can make code unreadable, which leads to:
coupling - it's often best to tightly couple things for ease of debugging and development. How often are you going to change the database you're using? Is it worth going through another abstraction for every database call? Too much abstraction makes code unwieldy.
I'd rather have a bunch of 200 line methods that represent logical units of work than a call stack 20 layers deep.
I often use GOTO statements in C code to mimic exception catching without duplicating cleanup code. It works very well, and its easy to understand, maintain, and debug. Example:
x = do_stuff();
if (x 0) { goto cleanup; }
y = do_more stuff();
if (y 0) { goto cleanup; }
return 1;
cleanup:
fix_my_mess();
fix_it_4realz();
return 0;
I know a lot of people think every method needs a goto, but I break that one when I can get away with it.
In short, I suggest that the programmer should continue to understand what he is doing, that his growing product remains firmly within his intellectual grip. It is my sad experience that this suggestion is repulsive to the average experienced programmer, who clearly derives a major part of his professional excitement from not quite understanding what he is doing. In this streamlined age, one of our most undernourished psychological needs is the craving for Black Magic and apparently the automatic computer can satisfy this need for the professional software engineer, who is secretly enthralled by the gigantic risks he takes in his daring irresponsibility. For his frustrations I have no remedy......
--Edsger W. Dijkstra
Variable and object naming, and commenting is an art-form that takes experience to do well. Here's a few practical guidelines I've learned follow:
1. Think of newspaper headlines when commenting. Don't make somebody read the whole article to know what the article is about.
2. Comment the "odd" stuff, not the obvious stuff infer-able from function name etc.
3. Goldilocks Rule: Names both too long and too short can be bad.
4. The more frequent a name is used, the shorter it should be. Use comments at declaration to give the full name. Example of a variable that may be used often:
var dhv_id; // Department of Housing vendor ID
If it's used often, I'd rather have an abbreviation than see DeptOfHousingVendorID all over the code, making it long and "wrappy".
5. Everybody has their own preference, but you have to target the "average" developer (future unknown reader) to make code future-friendly.
Table-ized A.I.
Bingo... the first rule is to be as zen as possible with your programming. Clean and efficient... no bullshit.
"Never give up, for that is just the time and place when the tide will change." -Harriet Beecher Stowe ^_^
Just one extra (or fewer) space per bracket...'cause I know it's driven SOMEONE nuts at every shop I've worked at.
Mainline thoughts are that long variable names are better since you can remember what they do. I like short variable names if they're just used for a short time and not continually used throught the global scope. Short variable names have the following strengths: quicker to type so code is written faster, doesn't make you do mental work of focusing on the variable name very long so you can stay focused on the code, and job security since your code can't be read by anyone besides yourself. That last part is mostly a joke.
God spoke to me
Fuck you, article writer.
Many of the rules in the article are about making the code easier to understand for yourself or whoever takes over when you have to leave a job or role. For example, you could put every single commands into one line; heck the entire program could be one really long line. I would argue it would be more efficient on disk space, but if you had to debug or modify it, it would be a mess. I don't code much but when I have to look over what I've done a year or two later, it's easier on me if I followed those rules.
Well, there's spam egg sausage and spam, that's not got much spam in it.
I love to use the ComeFrom statement. It just makes the logic of the program so much easier to follow when others need to apply that very rare patch to my code.
Since when is returning from the middle of a loop a bad thing? I've never been taught that, and it certainly hasn't been a guiding principle in any code base I've worked in.
Seriously people still use the goto statement and love it? I have never meet anyone that loved the goto statement.
From the article, there were 9 "Bad habits". Only one - "Programming habit No. 7: Breaking out of loops in the middle" could as see as ever making things more efficient. I also think that the article has Programming habit No. 1: Using goto and Programming habit No. 2: Eschewing documentation reversed in priority.
Come to think of it.... goto isn't as bad as bubonic plague.
#3 (overly long lines) largely depends on the language you write in. SQL, for example, pretty much forces you to either write far-too-long lines, or to use tediously tall vertical layouts. The former lets you focus on the bigger-picture logic of a procedure, while the latter only matters if you care about quickly knowing the 10th field returned in a given selection.
For #6 (reinventing data structures), gimme a frickin' break - I will not pull in a 3rd party library just to implement a data structure I can write in my sleep. Fortunately, most languages include just about every standard type imaginable in their standard libraries; but hell will get pretty frosty before I resort to an external dependency for something like a boring ol' red/black tree.
#7, flat-out guilty as charged. Yes, when the code has nothing else to do, I'll return to the caller from just about anywhere. That said, I do typically try to organize my code such that I don't need to return from anywhere other than the end; but when that means ending up with half a dozen layers of nested ifs just to avoid returning early - Just do it.
sometimes it's easier to cut 'n paste than trying to paramaterize everything -especially early on when your banging out a quick report of proof of concept
-although if/when I end up doing parameterization I have to hunt through the code and delete all the stuff that was obsoleted by the parameterization
-I'm just sayin'
And I have the bad habit of using the same variable names for everything, and something entirely unrelated to the variable. Works great - except for the day after.
There are a lot of bad "rules" running around out there. There's also a lot of good ones. Some have evolved through painful experience; others are more like cargo-cult beliefs. But the bottom line is that we're all terrible judges of our own work. That's why authors need proofreaders and (frequently) editors. If you want to break something you think is a rule, for whatever reason, try checking with your cow-orkers, to see what they think about it. Yes, they may all be hide-bound idiots, but if you get hit by a bus, they're the ones who are going to be left maintaining all that code you wrote. And maybe, just maybe, they'll spot something you didn't.
Yes, I know code reviews are painful and waste everyone's time, but spotting errors and issues up-front is orders of magnitude less painful than spotting them long after the fact, when the code has evolved to become several times more complex.
The longer or more descriptive the variable name, the wider the scope.
love is just extroverted narcissism
Combine that with another poster's 200-line methods and someone looking at the code has no easy way to know what the code returns - what it DOES . And the person looking at it my be you in two years. Also, such code -lies- about what it does. More on that in a minute.
It is better to return at the bottom of a function, partly so you can FIND the damn return statement. So an improvement over return () in the middle of the function is to break.
Even better, make the loop condition honest. Instead of this:
while(line = readline) { ... ...
if lineempty(line) return;
}
Do this:
while( (line = readline) && line_not_empty(line) ) { ...
}
Now your while() condition is honest, it truly reflects how long the loop loops.
We are hired to write new programs/new ways of solving a problem. Rules are made to solve common problems.
If we only follow these rules we are limited to writing programs that have already been written, in that case we are just useless.
If we know when to bend or break the rules, then we can create things that solve problems differently and is new and unique.
When I work with programmers so are hard fixed on the right way to do things, I often get a response that x cannot be done. I break the rule and I have done it in a couple of days work, then they will go but you didn't follow the right form.
The end user doesn't care about form, they care if it Works well, It can be maintained, and it is secure.
If something is so important that you feel the need to post it on the internet... It probably isn't that important.
The greatest sin I seen (in my code and that of others) is to focus so much on making the normal/success case clear and simple, that the error/failure case becomes even more difficult to follow. In many ways exception handling is a feature designed to encourage this mistake, since all that checking return codes made things complicated.
It seems that many in the field these days are afraid to code something themselves for fear that someone will find fault. So, they do things "the established" way, which is generally frameworks or anything that can be called "reusable", even if this generation's "reusable" is always less reusable than last, because it keeps getting needlessly more complex to the point that nobody *can* reuse it.
Used to be programmers had a fault we called "not invented here", in that they'd insist on re-writing things that already existed, because it was easier to understand their own code than to use someone else's. These days it's reversed. For fear of criticism, they *must* use someone else's code rather than write their own. I call it "afraid to invent it here."
I use Allman style braces.
https://en.wikipedia.org/wiki/Indent_style#Allman_style
I have found that the ease of being able to visually line up matching braces in the same column beats the fact that the resulting code uses a few more vertical lines. This is a judgment I have come to after 25 years of seeing both styles extensively in use. I use this style in every language that has braces, including JavaScript.
I really hoping you are being sarcastic because if not, you really haven't worked on any projects of real size. What you describe is really nice for an app that does addition and subtraction, but when you have connections with multiple databases, have several dozen static sockets connections going while processing messages from those sockets, your suggestion of not using objects is naive at best.
I see a lot of rigidity around buzzwords and established practice, because programmers these days are given tools, and to appear smart to other programmers, they treat these tools as rules. As if knowing some rules is more important that solving problems and getting the job done. It's apparently easier to show how smart you are by regurgitating rules and criticizing people who don't follow them rigidly than it is to actually accomplish things.
Using Perl pretty much covers all nine and then some.
Except, perhaps, for #5 Yo-yo code. That is actually a built-in feature.
Average Intelligence is a Scary Thing
I refuse to use class member functions or variables without the "this" keyword despite everyone telling me it's not needed.
I'd rather pollute the body of member functions with this->some_variable against just writing "some_variable" and expecting the reader to know if that's a member or global variable. Especially if it's a member inherited and not directly on the class where it's used.
I also refuse to use globals, or interact with any C api without first wrapping it in a trampoline that calls right back into C++ member code, properly wrapped with cleanup code and a smart pointer.
My USD $0.02c
Of course I was being sarcastic. Pretty much obvious, don't you think ?
But still - on Embedded world you can do much without relying on dynamic allocation (and the expensive free). Just needs careful sizing and requirements analysis.
Alvie
Add macros to the list. After inventing template types which shoehorn macros into the string C++ type system, dogmatists decided that the rest of the cases handled by preprocessor macros are somehow evil. Don't listen to them.
As for gotos. Linux driver people use them to clean up after jumping out of a sequence of system calls. I imagine BSD folks do as well. They use them like simple exceptions. Intent is clear and simple. I have no problem.
You seem a bit upset. Step outside and sniff a flower.
All my indexes are i, j, k, etc unless there's an actual reason for using a real index name.
Sometimes, I just want to fucking iterate without having to come up with some kind of descriptive name that's ultra-long and verbose. I'd use foreach if the language supports it...but it doesn't.
Yeah I just add int1 and int2 without even worrying about overflows. But so does everyone else. :-/
Cwm, fjord-bank glyphs vext quiz
The habit is pedantic, craps up the code, generally loses. When I encounter it, it is easy to fixed with a single replace. Globals (singletons) have their place, especially for map lookups, unless you prefer needlessly decoding argument references.
Eh.
I work with a lot of third-party code. A large framework that violates as many of these rules that are relevant to the platform, and adds a ton of new platform- and domain- specific annoyances.
It is what it is. At least bad code is still code. You generally have defined conventions and the tools available to track down problems, even if it is frustrating. (Not so with client expectations!)
Moreover, more and more code is, quite frankly, disposable. Only the business requirements need to be well documented because everything else is going to churn within 2 years. These problems are only going to get worse, and a "good" programmer will be one that can still work efficiently within the chaos.
I do get upset when I see idiotic articles like this. It implies that these "habits" are acceptable, when in fact they should be avoided unless you have a very good and specific reason to do so. For things like documentation, there is NO conceivable excuse to justify being so lazy as to not being willing to add a quick blurb about what your routine does, *especially* when it's doing something unusual.
I have the bad habit of adding bugs when I write code. I must be absent minded or something while writing code because once I'm done and detect that there is a bug, I can never remember adding it. Sure I fix the bugs before you ever encounter the code, but just think about how much time I could save by controlling this bad habit. If anything, that got to be the worst bad habit of programming ever.
That is one rule no-one with any pride in their work would break, unlike, ooh, say ...
Money grubbing buggers.
Many functions and even some classes are more or less self-documenting. Functions with names like insertReservation or cancelReservation or deleteAll don't need another line or three to explain what's going on.
An undocumented function with well chosen names is like a sentence with only nouns. You might, if you're lucky, get the gist of it but those missing verbs and adjectives would really have been helpful.
Moderating "-1, Disagree" is simple censorship. Have the guts to post your opinion.
I read the article. Honestly, it sucks. And I should have known it. Anyway, to let come something good about it, here are his arguments:
a) gotos are only a problem when the result in entangled code and in some cases they make code more readable, because you may avoid if-then-else trees.
- This is wrong for a number of reasons: Too many nested if-then-else result in too much complexity and violate another coding style, ergo such structures are already bad in the first place, to fix them with a goto makes things not better.
- Many languages used nowadays do not provide a real goto. Even though Java has jump labels, but should not use them!!
b) Commenting all functions is wrong.
- Well this is true, however, it is also not a rule. It was a rule in the days where your function names where limited.
- You should document functions which have side effects, and you should document the purpose of classes, modules, etc.
- Never document after writing. Write the documentation first, let it explain what the implementation should do and why, then write the code, then fix the documentation
c) Jamming too much code on one line, i.e., do not write component.interface.orderSushi()
- I do not know where the guy is from, but that is not against any rule
- While he is right here, which jerk still uses such limitations? Checkstyle, PMD, and Findbugs have no problem with what he wants. And yes your line should not be that long that you cannot see the whole statement/expression.
d) Not declaring types, he means not using types for declaration
- In languages, like C and Java you must give variables a type
- in Xtend the type can be inferred in many cases, then you can skip them in the definition. Yes you can, however, sometimes it is still more useful to type explicitly, because it prevents a variable or value to be used in the wrong way.
e) Yo-yo code, he means do not convert numerical values to string to pass them to some code and convert it back afterwards
- This is a perfect and good advice. Actually, it is an advice for a moron. You NEVER do that.
- However, it makes sense in his example, because if you are ordered to use a library which uses strings you must convert, but that is not a violation of the do not make stupid moron code, it is the "bad luck, you have to interface with some moron's library" situation.
f) Writing your own data structures is a bad choice
- First, this is utter nonsense, we write new data structures for every program. Where is this guy from?
- Second, he means storage routines. Well that depends. If you want to write your own OR-mapper then that is a wast of time. That will never be faster and never be able to work with any scaling technology.
- Third, I cannot see a real clear argument in that section, but maybe he is right, because it is Tuesday.
g) Breaking out of loops in the middle
- That depends on the language. If your language has no garbage collection mechanism or you passed references to data and changed it, such jump out could leave you with an incomplete state. However, we do not teach that this is illegal or bad practice, as we teach Java at university. It could be stupid, if this results in a lot of code duplication in the dependent return (if (bla) { code; return rubbish; }
h) Do not use short variable names
- Yes you should not use short variable names, when they do not express what the variable does.
- However, i,j,k are iterator variables. We know them from math.
- In contrast f is not a good variable name (in most cases) so better call it fartCounter or even better countFarts, depending on the purpose in your game
i) Redefining operators and functions is evil
Yes it is. And no there are no good reasons to do so as a programmer unless your redefinition is WELL documented and stays the same in the whole program. It is already stupid that Java uses the plus sign for String concatenation. Especially, because String is a calls in Java and not a data type. However, it sucks even more if the + sign or * sign is redefined and means one time multiplication of two scalars and the time it is a cross product.
It might be more code to type (or to provide by the IDE) to actually write a function name, but please do so, it increases readability.
i write php, mainly for my own use (a little for the web). i even use it as a replacement for bat files in windows (file manipulation and such, not interfacing with windows like bat/cmd can). i use procedural code a lot. essentially only using oop when existing libraries i'm using are built that way. and i dont give a shit what people think, because it's for me, my stuff isn't complex, and it's easier and faster to produce.. if procedural code wasn't "good enough" languages would no longer support that method.
You are right in everything you say, and you sound like a self righteous prick. Guess what? I'd fire you in a heartbeat.
Code: I can help you fix coding habits.
Attitude: Everyone's happier without it.
presume 99% of other programmers are idiots!
never failed me yet!
and yes, you are the 99%.
You should all be forced to work with computers with too little memory and slow processors.
You might learn how to produce efficient, fast and small code.
Sometimes I name database tables in plural if the singular name ends up being a reserved word in SQL (ie. 'orders') and I do it because I'm sooo damn lazy but it does make writing queries easier. I'm so ashamed so posting as anonymous coward.
Peter Wayner doesn't have a clue about programming. Why is he publishing articles on the subject? Hint: TRUE and FALSE are not keywords in Python, and you can do the same ting in most other languages too. It's just stupid though and no sane dev ever does it. Way to go, Wayner.
I love a good ternary. I find it really obnoxious to try and set a variable through a long set of if-then-else statements, and sometimes I'm forced by C++ const correctness to do it through a ternary anyway.
I do my best to break them up over multiple lines in a way that makes the structure obvious, and I document them so you can read the comments instead of reading the actual code, but I know that's vulnerable to comment-rot.
To a certain extent it's a bad practice, but I really do find the terseness of a ternary a lot more pleasant. (If I have to do it more than once, I just make it a function, obvs.)
Did you reply to the wrong post? I posted about a loop, you posted about a conditional.
Your post contains no loop, and mine doesn't assign twice.
Or are you referring to the fact that in order to return at the end, one might have to assign the return value to a variable earlier. That's true. Compare spending 1/1,000,000,000 of a second processing time versus both spending 30 minutes of developer trying to figure out what it's supposed to return time AND increasing the likelihood of a bug affecting customer data. One is clearly better than the other, certainly.
How exactly is it self righteous to expect someone who claims to be a software developer, to actually be skilled in that field?
How is it self-righteous that a client's project is gonna crash and burn because they are using developers that *don't* follow best practises, and end up vomiting some kind of nonsense that vaguely resembles intelligible code?
I've HAD people fired for being grossly incompetent, to the point where they were a threat to the project. And you know what? They were perfectly nice and friendly people. That doesn't change the fact that they shouldn't have been allowed anything more sophisticated than a burger grill.
So yeah, I dunno if you're actually a project manager or just some AC putting on airs, but if you would rather ride your mission critical project on someone who was "nice" rather than someone who actually knew what they were doing... Well... I look forward to reading about your projects on The Daily WTF.
To actually be honest (same AC as @3:59PM), I agree with most of what you said. Not all the "habits" in the article are actually necessarily evil - for instance, I very often do multiple returns from a method, not least as those methods tend to be about ten lines long and the whole thing is in your face at the time. I disagree with coating code with comments, but *definitely* agree that anything non-standard, obtuse or unexpected bloody better should be commented and I get at least reasonably angry when it isn't.
We have methods that are ultimately, A = B + C * D, where A and B are 1 x n vectors, and C and D are matrices n x m and 1 x n matrices respectively. These methods span thousands upon thousands of lines, and I'm fairly sure that some careful usage of boost::matrix would reduce them to about 30. If there was a reason to make them so absurdly overcomplicated, it should have been explained. It wasn't, and it's a source of confusion and bugs to this day.
I never put braces around one line stements in for example if statements.
if(a==b)\
{
do_a_equals_b();
}
I feel an overwhelming urge to expand and read these functions....well played, sir.
HA! I just wasted some of your bandwidth with a frivolous sig!
...that there's no universal principle or rule that's always true? Could it be that the best thing to do in a given moment, is to do the best thing in the given moment? naaaaahhh....
Hmm speaking of bad code... replies linked to the wrong comment. Nice.
what rules, to begin with ?
Do you always solve the same problem ??
If I know exactly what someone wants, then sure: write the tests first, that makes sense.
But the vast majority of the time, fulfilling a requirement is just as much about finding what the person *really* wants, as it is about finding out how to supply it.
By the time I know what the question is, the answer has already been written.
It's only at that point that I have the ability to rigorously test the implementation of that answer.
-- 'The' Lord and Master Bitman On High, Master Of All
Although I was unaware Java had those labeled jumps. Funny, given that I've been a Java coder the last 15 years.
A Stackoverflow answer had a decent example of where they could be used; a straightforward nested loop that quits when it finds something. Especially with foreach that doesn't look too bad.
search:
for(List<String> names : groupNames) {
for(String name : names) {
if("joe".equalsIgnoreCase(name)) {
break search:
Horrible? Maybe if the average coder hasn't seen labeled breaks before...
Common sense trumps absolute rules.
I use builder classes, which apparently a big violation of C++ style guides. For example class StiffnessMatrix would provide lots of services and provide methods to interact with other instances of StiffnessMatrix. But it will be built using a class StiffnessMatrixBuilder. I don't know people say things like "a well designed class would not need a builder class".
sed -e 's/Chuck Norris/Rajnikant/g' joke > fact
without a 4K monitor.
I can now compare whole code segments, in many instances, side-by-side in full, and my brain loves me for it.
My bad programming habit is #3 -- putting too much code on one line.
The problem I have is that the there's no standard terminal width anymore. Back in ancient days, it was simple to say "just try to keep lines less than 80 characters long so that the fit nicely on the VT100 terminal everybody uses", but now that most developers have monstrous 30" LCD screens running a 4K resolution, etc, an 80-character line-length limit is a ridiculous underutilization -- like formatting a newspaper so that it can be easily read through a periscope, even though nobody ever uses periscopes for that purpose.
So the next thought would be, choose a line-limit greater than 80 characters long. But what? 160 characters? 320? Whatever number of characters fits across the width of a "typical" window? What constitutes "typical"? And at what font size? Fixed-space font or proportional?
At that point the Asperger's-compliant section of my brain says "ah, screw it, it's all subjective anyway", and I just end up ignoring display-width issues and just putting as much on the line as will logically go onto the line. vi (the only editor worth considering ;)) handles line-wrapping just fine, so it's not a problem for me. It does irritate my co-workers who are using non-line-wrapping IDEs though, since they have to do a lot of scrolling back and forth. (Of course that has the benefit of training them to leave my code alone if possible, which shouldn't be overlooked)
I don't care if it's 90,000 hectares. That lake was not my doing.
I don't care if your variable is a loop variable or a simple array. Ever try searching code for a loop variable "i". Yeah, that's why you don't do it. At the very least, name it "ii".
This comment brought to you by Slashdot's baffling omission of an 'Undo moderation' feature.
I break the rules if I am out of time and result is better than before. E.g. I refactor without writing new tests.
Comprehensive unit tests don't help when the thing only breaks in integration testing.
Maybe the sqlite database you use in your unit tests behaves slightly differently than the PostgreSQL database you use in production.
Maybe the refactored code adds a race condition that the unit tests don't cover.
Maybe the refactored code runs slower in certain scenarios that turn out to be important.
Maybe the unit tests missed a corner case.
Sure, in an ideal world these wouldn't happen. But they do.
Now do the nested version when you're trying to allocate multiple resources independently and need to clean up all of them if any of them fail. Oh, and it needs to fit within 80 columns due to other coding rules.
Gotos are a clean way of doing error handling.
The "single point of exit" can make some types of debugging easier since you can hook the exit with less work.
There's another argument against comments I occasionally hear as well, which is "a competent coder should be able to discern what the code is doing without comments." While that's technically true, it's another argument I would reject.
Me, too.
Sure, a competent coder can tell what the code IS DOING. So can a compiler. That's not the issue. What do you do if what the code is doing is the wrong thing? What is the RIGHT thing? If all you've go is the code, you're hosed.
NO process can look at code alone and find bugs - because correct code for one thing is not correct for another. A perfect implementation of "cat", for instance, is not correct when what you want is "echo". All correctness checking can do is compare two (or more) expressions of the intent for discrepancies.
I feel the best comments can and should declare the intent of a block of code, rather than drilling down into the mechanics of the code.
And that's what they're for. When writing multiple expressions of intent, the less similar the languages used are, the less likely an identical error will end up in all of them.
With the comments you have a chance. With allegedly "self-documenting code" all you can test is the compiler.
Bantam Dominique roosters crow a four-note song. Once you've heard it as "Happy BIRTHday" you can't NOT hear it that way
I do this often when in a hurry or when I'm pretty sure the code is only going to run on the intended platform (always embedded mcu), when for instance a data frame is received with the same endianness as the platform i can extract a word like *(uint16_t *)pBuf instead of doing it with shifts and addition. Stuff like that.
I never break out of loops or ifs with a direct return.
It somehow makes me cringe in a very uncomfortable way.
I always return one level up, even if it means using a utility variable (often called returnMe and initialised at the beginning of a function).
I couldn't say that that is much better, but it does make me *feel* a lot better.
As for more good habits:
I've recently gotten into more serious function programming territory, trying to avoid variables entirely. Doesn't always work and gets your head all in a knot, but you learn a lot. And it is faster - both in coding and at runtime.
We suffer more in our imagination than in reality. - Seneca
Beautifully crafted over-engineered OO spaghetti featured dozens of widely over-documented classes that feature every theory the coder learned at school bolted onto a time saving framework that another artisan wrote to prove something.
And not a single spec doc so nobody knows WTF it is supposed to do.
int main() {
unsigned int bullet=1;
headshot();
headshot();
bullet--;
return 0;
}
Now you have.
The best way to get a bad law repealed is to enforce it strictly - Abraham Lincoln
"Coupling" is a vague concept, I have found out. Specific "types" of coupling could be called code patterns, but coupling as a general concept is vague (as currently described by authors).
As far as function/method size, in my opinion "it depends". If it's quite short or quite long, at least pause to ask "why" and consider alternatives. But don't force every one to be 20 lines out of some ritual habit.
My "best" shorter methods tend to be called many times. They often consolidate commonly-found domain idioms into a simpler concept.
Table-ized A.I.
"We've thumbed our nose at the rules of good programming, typed out code that is totally bad -- and we've lived. There were no lightning bolts from the programming gods. Our desktops didn’t explode. In fact, our code compiled and shipped, and the customers seemed happy enough."
No, but breaking programming rules doesn't cause a problem today. It causes one tomorrow. It's akin to gluing a loose electric plug to the outlet. It solves the problem (very well!), but when you later need to unplug the TV, it's going to be needlessly difficult.
If the unit of work provided by a function changes, then a programmer who understands what maintenance is like will also update the comment describing what it does.
My goodness, people! How hard is it to declare what something does? Get off my lawn!
Working at a software development firm where the programmers are almost all folks with engineering degrees, I not only feel your pain, but experience it nearly-daily. We'll have cases in our bugtracker that say "fix {programname}", then the commit will say "fixed" and the case will be marked closed without going to anyone for testing. Then I'll run into a bug that traces back to that commit.
I rebel by being extremely verbose in commit messages and emails and such whenever the fancy strikes me, and when the engineers get upset I just retort that I'm averaging things the fuck out to what they should be!
The funniest part is when they complain about some since-departed programmer's code being so mysterious and unreadable and hard to figure out why it was written the way it was. If only they had documented it, you say? Sigh.
I remember sigs. Oh, a simpler time!
Pick any two at once
The "standard" OO rule you talk about is a kludge due to learning OO in languages like Java. In Java it's impossible to control or intercept access to an attribute, so once the cat's out of the bag, you need to wrap each such access in an explicit getter/setter.
Attribute access (obj.attribute = 1) is more elegant and easier to read, and is actually consistent with other OO encapsulation practices like "obj1 + obj2" is easier to read than obj1.add(obj2). In Java, once the client uses the attribute, you're blocked from doing anything more advanced with it. In Python, that's no problem - descriptors/properties are specifically designed to intercept attribute access and the caller doesn't need to change anything, so obj.attribute will call an instance getter method if you want it to.
The current crop of Java programmers who write Java in Python go on to generate getters and setters and abstract classes and other Java idioms, resulting in ugly unmanageable code that exemplifies the very worst of both worlds.