Defining Useful Coding Practices?
markmcb writes "A NASA engineer recently wrote about his disappointment that despite having well-documented coding practices, 'clever' solutions still made the code he has to maintain hard to follow. This got me thinking about the overhead spent at my own company regarding our code. We too have best practices that are documented, but most seem to focus on the basics, e.g., comments, modularity, etc. While those things are good, they don't directly ensure that quality, maintainable code is written. As the author points out, an elegant one-liner coupled with a comment from a few revisions ago makes for a good headache. I'm curious what experience others have had with this, and if you've seen manageable practices that ultimately offer a lot of value to the next programmer down the line who will have to maintain the code."
Simple, clever doesn't pay the bills, reliable and maintainable do. It's a cost benefit analysis: if that clever one-liner saves many man-hours of work, then probably a good idea. If it saves you two or three lines of code and all of an addition 45 seconds, probably not worth the blow to maintainability and readability. It's always a tradeoff...just have to decide which is more important in any given context, and at most companies, reliability and maintainability is king compared to a slight runtime increase or 45 seconds/3 lines shaved off.
As the author points out, an elegant one-liner coupled with a comment from a few revisions ago makes for a good headache.
A oneline that had multiple revision should be completely rewritten, because if you manage to get multiple changes in one line it surely is a mess... somwhere.
To my mind, the best coding standard is Don Knuth's Literate Programming: http://www.literateprogramming.com/. Quoting:
In my company i am the sole/lead/only developer, and as such i have my own defined best practices and i follow them adamantly, that said i have written clever couplets of code that "just work" they are commented from here to the nines yet 4-6 months and 3 projects later when it comes time to look at reviewing for a possible update in the project i can sometimes look at it and have to spend extra time to follow the elegant code around to get it "loaded" back into my head to see what i did. i think that so long as programs are written by humans who can sometimes look at it sometimes as an art form there should always be an "elegant code" overhead coefficient worked into every project update.
A person whose code I regularly inherit seems to hate functions. He just writes subs with no parameters and uses global variables to pass information to and from them. It's awesome...
"Quality" can be both objective and subjective, and it seems this post is leaning towards the latter in terms of what it's getting at... while I believe in having formalized guidelines of some kind, I've found the best way to seek out and improve the elements that can't be easily quantified is through (drum roll) code reviews/walkthroughs. It seems like these are rare, at least where I'm at, and it's hard to get buy-in when you're talking about contractors charging by the hour, but in my opinion a single quality code review can save TONS of time down the road and is necessary for projects over a certain size. Also, read "The Pragmatic Programmer" and "Code Complete" for some of the best guidance on this topic.
Incorrect documentation.
... trust .. but verify!
.. then the documentation is unreliable at best, and dangerous at worst.
And the only correct documentation is the code itself. Anything else is a opinion and should be viewed accordingly.
In other words, when it comes to reading documentation
I don't know how many times over my 30 year career that I've read documentation and started work only to find out later that it hadn't been updated. The first standard in your documentation rules should be that all relevant documentation is created and updated before code goes into production. No excuses.
If it doesn't have that
I rarely read replies, it's my opinion and if you thought about your opinion a little more, I'm OK with that.
Review the code that gets checked in (you are using version control, are you?). If it doesn't obviously do what it is supposed to do (including the case where it is not obvious what it is supposed to do), something is wrong.
You can spend any amount of time writing best practices, but that doesn't ensure they are good, workable, and actually applied. In the end, what matters is if other people can understand the code. And you can measure that by just having them do that. As an added bonus, you get more people familiar with the code.
Please correct me if I got my facts wrong.
90% of code is rubish. Which should come to no surprise since 90% of everything is rubish.
The point here is that there're not so much qualitatively different high-level solutions to "the art of programming". The fact that after 30/40 years of repeating basically the same (obvious) things most of us can't cope with its basics shows the obvious: 90% of coders are rubish. The fact that 90% of coders think about themselves to be in the other 10% doesn't help, either.
Works for congress.
I wouldn't ask here. Some of the worst code advice I've ever seen comes from Slashdot, especially from the idiots who think all programming is like their PC or Linux box. The worst are those that think Java is suitable for everything, or that what's good for the web is good for every situation.
Even if they themselves aren't useful, they might give you an idea of how crap or "clever" the code is at some point ;).
;).
Anyway, to me one very important good coding practice is using standard or defacto standard libraries where possible instead of rolling your own (unless it means using those libraries in "rube goldberg" ways, in which case roll your own).
Because it means less to document, maintain and also it makes for easier debugging - because when you see "standard library method/function" you know what it does, you don't have to look it up and check it.
Whereas if the program is filled with custom library methods (even when standard ones that do the same thing exist) you will have a lot more work to figure out why and whether they do what you think they do and what they are supposed to do.
And that's why in the real world, writing in certain languages is better than in others, if it means you end up writing less code of your own. It doesn't matter if the language is so cool and powerful that you can write code that does zillions of stuff, but if it means you have to rewrite lots of stuff that would be standard in other languages, it's not as maintainable.
Yes in some scenarios you want a powerful language that helps you in the code that you write. But in most scenarios, you'd want a language that helps you in the code that you don't have to write
I fear that he is really trying to bring everyone down to the level of the novice programmer rather than raising the standard. I have met managers like that, they say things like ''we won't do that here'' (meaning: ''I have not seen that construct before and am afraid of something new''); ''goto is forbidden'' (meaning: ''someone important said goto is harmful'' - not having understood that Dijkstra was talking about code where 25% of statements were goto - in the days fore things like while loops).
We need to educate programmers, make them better - not dumb down programming.
I work in an environment where there are 4 programmers, of varying skill. What is "clear" to one is not necessarily clear to another, even if it is straight-forward coding. For example, I might program a loop to iterate through an array of data, extracting the important information, while another might explicitly write out each iteration, because he is less familiar with arrays. His code is quite clear - but inflexible, and subject to errors if he makes a required change to 19 of 20 copies of the calculations. Mine is quite clear, but subject to some obscuration if there's something special that has to be done in 3 of the 20 iterations.
In the case of TFA, if the author were this other programmer, he might consider my code to be "too clever".
Much was made of the use of "obscure" variables. Yes, that's one I dislike a lot, too... but I haven't made too much progress on that score in 25 years, with regard to others. When you're working on code that requires a lot of manipulation of a variable, typing a long, descriptive name 65 times is a bit of a PITA, and subject to its own bugs, when you misspell it a few times!
Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.
The practitioner of literate programming can be regarded as an essayist, whose main concern is with exposition and excellence of style.
The language which provides the capability to achieve all these goals is called COBOL. Ever heard of it?
I'm baffled. Here we use extensive extra documentation, mostly outside of the code. Programming changes are related to changes in design documents and deployment documents. Also most of the design follows UML diagrams which are thoroughly kept together with all programming code. As far as we use programming code, every change is reviewed by another developer. Without these, we would quickly lose our minds, heads and bodies to the massive complexity.
I always thought NASA was one of the top quality software engineers, but apparently not in every department.
This is a replacement signature.
the op has it mostly right. There's little value in fancy show-off code that may earn a programmer some machismo points from like-minded colleagues but results in maintenance headaches down the line.
Elegance is often misused to mean terse, clever code, but that is really far from what elegance ought to mean.
I define good, elegant code to be code that is clear, well commented, self-explanatory, and easy to modify.
I'm dealing right now with some "object-oriented" Perl programs that are nearly comment-free. Sure, eventually it'll start to look familiar and I'll know where to go to fix stuff, but it pisses me off at the programmer.
I don't want people cursing and mocking my name after I've left a position, so I always strive to make my code as obvious as possible, at the cost of some high falutin' fanciness that just bogs people down and keeps a company from getting its business done efficiently.
To me, the highest compliment is when a newbie says "I just read your {Java|Perl|C++|SQL} program and I was surprised at how easy it was to understand. I just wanted to thank you for that."
it's = "it is"; its = possessive. E.g., it's flapping its wings.
Why should I write my code so that the lowest-common-denominator dull thud who took CSci just for the money can come along five years later and maintain it? I agree that good coding practices produce readable code. But too often this whole topic leads into the idea that all code should be McCode, meaning blah standards-conforming blocks, often not even approaching the best possible solution to a problem.
Sorry. Not a priority. And no, I don't care that you won't hire me. You want an assembly line slinger. And will pay accordingly.
Traditional everyone set aside time and review checked in code is hard to do, difficult, and time consuming.
On the other hand, automated code review tools are life changing. There's a bunch of tools out there, but the one I think is far and away the best is Google's Gerrit tool (http://code.google.com/p/gerrit/), which is what Google uses publicly for Android.
I cannot understate how helpful Gerrit has been in this regard. So many things that are trouble down the road are easily caught by even just one other pair of eyes. Everyone who has used Gerrit at my compnay has fallen in love with automated code reviews. It's refreshing, leads to better code, etc. I seriously could gush about Gerrit for pages.
As we all know just about every coder has their own way of doing things. Each person has found the solution to common programming steps differently. This flow of logic is carried over to larger programs and new logic that must be considered to answer more complex coding problems. There is no one way to write a program and this means there are a thousand different ways each person could come up with the logic paths to answer the question. Clever cures boredom and does what every programmer should be doing...making their program more efficient....
What makes sense to one person is a combination of how they answered similar problems and other coder's answer to similar problems. There are many hacks in the industry that cause a lot of problems for code readability. They are not clever, they are the other extreme where they barely understand what is even going on. Any real coder though can see the difference between being clever and being a hack, it usually involves comments carried over from what program they gutted to get their's to work. I think it's important to separate those two types of problems.
Once you take out the hacks you should be able to see how any coder reached the logic they did. Rewrites because of logic doesn't just stand at the one coder required to maintain class xyz. Shit falls down hill. If the overall design is flawed, if the requirements are not clearly outlined, if someone high up keeps changing their mind..then the coder at the bottom of the hill doesn't stand a chance at writing long term good code. Anyone that's worked in a project knows there is this odd relationship that forms. You have the heavy coders, you have the logic gurus looking at the bigger picture and delegating, you have the do what you cans that are left the scraps after the heavy coders get their work, you have the project leaders which hopefully will be writing code too, you have the project owners that write no code, you have the upper management barking orders. There isn't just 1 person responsible for good code even if there is just 1 person writing it.
One person's clever, obscure trick is another person's common practice.
Communicate with the other coders in your project. Write decent comments. TALK to the other coders. Cooperate and share ideas.
Optimization is fine, as long as you're "optimizing" the code to be more clear. This is a good way to redirect the energy that programmers often put into pointless performance optimization. Most understand that optimizing for one thing often de-optimizes somthing else, so they understand that you can't optimize for speed and clarity in many cases. Always looking for ways to make code clearer can become an enjoyable habit, as optimizing for speed is for many. Then you spend your idle moments eliminating many lines of code that you realize are unnecessary, bringing the code closer to its essence. My experience anyway.
At the last few places I've worked we found that just ignoring this problem long enough lets it just go away. People who have style and coding practices that are bad, end up changing them eventually or leaving. And people with good practices tend to have success with their software, even if the styles end up being different from one another.
If you can't wait for the problem to sort itself out, then try hiring a bunch of good people that have worked together before.
“Common sense is not so common.” — Voltaire
When will people finally realize that defining and documenting practices alone doesn't make good code? You need good programmers that care and that monitor each others and (more junior) programmers' code quality. Encourage feedback between the programmers, etc.
is releasing new functionality as quickly as possible. Timelines counts, not code quality.
One of the classic arguments between manager and coder is about how much time should be invested in cleaner code, since, as most coders would argue, such investment earns itself back because new features can be implemented faster when having a nice and tidy code base.
Managers of course often get their ways and coders reluctantly give in, mumbling about code becoming unmaintainable.
Investing in code quality is indeed risky:
- Even despite poor code quality, the coders know their stuff and implement new functionality without any problems;
- After investing in code cleanups it may soon turn out that this particular functionality is no longer required and all this nice and tidy code has been developed for nothing;
- "Cleanups" may end up in overly complicated code which turns out to be even more difficult to maintain than the original code base.
My karma ran over your dogma
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it."
There is no such thing as self-documenting code. A block of code without comments, no matter how well-written, is an order of magnitude slower to work with than code with just a couple of sentences at the top explaining what is going on and why. I find this "comments are always out-of-date anyway" argument baffling. If that's the problem, then the solution is to make keeping comments up-to-date a key part of the requirements, not to omit them entirely.
If someone lacks the discipline or intelligence to update a simple sentence after making a change, then their 'self-documenting code' is not something I ever want to see.
for(ss = s->ss; ss; ss = ss->ss);
Pretty standard way to traverse a linked list.
Using good source control helps, a lot. I've gotten very fond of git and its ancestror for style though not code, BitKeeper, because they let you do work locally and submit it centrally when you're ready.
Also, if your "coding standard" isn't built into your editors and common tools, you're doing something wrong. For example, if your C++ standard is not consistent with 'indent', you're doing something wrong.
erating is fine, especially if it's in a struct, to give you the context, or when it's first declared: int erating // employee rating
risk() is also better. Since the author says its a method, it's in a class. Investment.risk() is a hell of a lot better than Investment.calculateRiskInvestment().
The author is complaining because the code is all in c. Awww - c has different style conventions. Don't try to make it look like java.
why do we call this 'clever'?
clever as opposed to what, stupid?
maybe 'obscure' would have been a better word...
imho, the Linux Kernel Coding Style is the best.
First, I suggest a perusal of Capers Jones' book Software Assessments, Benchmarks, and Best Practices, from which we learn that most of the cost of internally developed systems (the sort TFA is talking about) is in maintenance. So there should be no argument that making the code maintainable is of some high priority.
[f/x: get off my lawn] Sadly, there are many programmers who don't understand that maintenance is not the act of rewriting the system in a language they would like on their resume. That might be adaptive maintenance, but the lion's share of maintenance actually involves fixing that which is broken (this would be corrective, perfective, and preventive maintenance).
Sadly (again) we already know the answer to the OP's question. You need an enforcement mechanism. The cheap and easy way to implement that is via code reviews. And we know that isn't going to happen.
Why? Because too many people who get to make decisions don't want to believe that first item from Jones' book. Making an innovative change to a UI is sexy, makes it obvious that some work has been done. Bullet-proofing user input edits is mundane, and smacks of cubicality.
You are unlikely to get a commitment from management to let you do things "the right way." If you did, you would have no way of knowing that it won't be rescinded when the boss' kid gets a job programming in the cubicle next to you.
The only way I know of to make this work is peer pressure. And that is unreliable. Thus maintenance will always have at least some aspect of the "fix what the clever kid did" to it.
Coding style is a no brainer for most languages: Use the default that your IDE provides. Or lacking that, whatever the "prittyprinter" program produces. If someone violates the style convention, the IDE or pretty printer will clean it up. Design reviews, code reviews, and updated documentation are unquestionably good ideas but they have to be budgeted in at the start. A project manager who's afraid of missing a delivery will treat them as slack in the schedule and let them go even faster than they will squeeze the testing schedule (but that's another story).
The author of TFA seems somewhat confused and inexperienced.
Find free books.
for(ss = s->ss; ss; ss = ss->ss);
Really? An idiomatic linked list traversal is too hard to understand? Does this guy think find postfix increment (++) too confusing too?
I admit though it is confusing if the semicolon at the end isn't a typo. Is this straight C or C++? I can't imagine that statement actually doing anything useful unless there's some kind of C++ operator overloading going.
There is increasingly a meme that pretty code in a strongly typed language id OK, everything else is CRAP.
//increment i
Well no, neatly written code, in whatever, is good, as are good code and clear comments where they are needed, ie NOT,
i = i++;
Some of the worst code I have seen written is in C++, Java and C#. OO over the top, full of OO jargon, code that wanders around a thousand method calls without solving the problem, and TOO MUCH CODE, zillions of complex libraries and dependancies.
Clarity, order and simplicity are the secret of writing good code, not language, tools or methodologies, and then it can be written in anything from Lisp to C#.
Our workplace is such that everything has to be easy to follow and understandable with a glance. This is not necessarily because someone else will be working on our code so much as that we almost certainly end up coming back to it 6-12 months later, or that the person writing it won't be the person maintaining it or porting it.
If it isn't easy to follow, you can usually fix this by spreading out the code and inserting more comments, and avoiding shorthand statements like inline ternary that save typing, but don't enhance readability. Inline comments are uncommon, instead varying kinds of flower-boxes before a section of code. In general, it ends up that being clever requires much more commenting in the flower box to explain functionality, so in the end it becomes a wasted effort.
A few days ago I saw someone going around with this sig from Brian Kernighan;
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
Non sequitur: Your facts are uncoordinated.
I've worked with teams a large percentage of (supposedly) hotshot programmers, and on teams with a similar percentage of mediocre talent. In my experience, it is the team with the so-so developers that deliver the more maintainable code. Why? I think it's because they know their limitations and are not afraid to "talk out the process" of writing code. They ask for feedback and opinion.
What that leads to is a collaborative development process, where everyone has some idea of what the other is doing. And in these environments, for some reason, people take ownership and responsibility for their code, end-to-end.
Contrast that with the hotshot teams: they know too much to ask for help, resent questioning of their implementation, mess around in other peoples domain "because they know better", and engage in heated arguments over trivial or religious matters. And in the final analysis, the lack of cooperation leads to disfunctional interfaces between components, idiosyncratic code, and incomplete functionality or low-quality performance.
Whereas the so-so teams learn to collaborate to get things done, the hotshot teams rely on heroics: they take on too much, show exaggerated progress by declaring their code complete even though it fails edge cases, and then spend outrageous overtime fixing 'bugs' (or worse, they're too important to work on bugs, they do features, so the lesser mortals on the team get to clean up after them). Because bug fixing doesn't get scheduled up front, the schedule slides and more work hours are demanded to catch up (with decreasing effect).
To my mind, the MAIN criteria for arriving at a maintainable codebase is OWNERSHIP, OPENNESS, and COLLABORATION. A collaborative team can do more in 40 hours than a heroic team can do in 60. They just don't look so impressive.
If you post it, they will read.
The one job that I did have as one made me realize exactly how uncommon it is in corporate-level work to find code with nice, detailed comments describing what's going on where. I will blame the lack of comments for one project I was assigned taking months to complete rather than weeks or days, especially considering that it was pure Java Swing code (i.e. no NetBeans), which is especially infuriating to work with.
http://www.faqs.org/docs/artu/ch01s06.html
This post expresses my opinion, not that of my employer. And yes, IAAL.
Changing someones development practices is a kin to changing any other behavior, I.E. hard to do.
I've found that code reviews offer a good barometer of where everyone is on the behavior chart but it is not enough to make a significant change to the behavior.
Here are few items that I find essential:
1) Maintainability Must be a priority: The architect\tech lead on the project must make this a priority. Simple code must be reinforced at all stages of development. This means new development must wait for previous development to be refactored into simpler code.
2) Shared understanding: The team MUST recognize the importance of this requirement. If they don't then the constant reinforcement will seem more like nagging, nit-picking than anything else. Have the entire team read "Clean Code" send out the Daily WTF articles. Jokingly pass out the bad code offsets.
3) One on One Coaching: Often complicated code is written simply because the simpler solution was not obvious. To help overcome this, team members should help each other with refactoring efforts. As an example: Interfaces and factories are often seen as "To complex to be simple" by junior developers. Consequently, it is the responsibility of the entire team to help teach why these "more complicated" solutions are actually much simpler and cleaner than the alternative.
4) Constant Communication: As with all creative work, whether you've accomplished the goal is very subjective. This means the team must have constant discussion, and sometimes debates, on what is maintainable. If you are the tech lead, be sure to show examples of your own code that is sub-par, and then discuss how it can be improved. If you don't have samples of sub-par code, you need to start writing more code. I can't emphasize enough that the tech lead must use their own bad code as examples, this helps create an open environment where people feel free to make mistakes and ultimately learn how to write better code.
Well that's my two cents as I sit here getting ready for Football to start. (c:
TDD, TDD, and more TDD. If it's too clever to maintain, than it's not testable. When done properly, test driven development functions very well as a guard against that sort of thing. The converse is also true, by the way. If something is very clever, but written in a testable way, than it's likely to be maintainable and easy to read. Even if it's not easy to read, the tests allow it to be refactored into an easy to read form without breaking anything.
Perl has probably got one of the best coding analytic suites in Perl::Critic. It addresses numerous areas dealing with Best Practices and addresses certain types of bugs. It is also highly configurable and easy to write your own rules as well.
The hardest part was actually getting people to use it. The fact is, there are a lot of people who have a ego and rather inflexible about changing their habits. The other side of the business will not care unless it can be shown it is a common practice or directly impacts money. So it is a tough battle to fight and most people are just not willing to get involved with it.
...for web development. And the primary goal of this platorm is to make doing typical things very straightforward, and easy-to-do-right and hard-to-do-differently. Commenting teh code itself is completely useless because the code reads fluidly. So comments are limited to what isn't programmed, or what has been removed for good reason.
Some advantages are quite clear when it comes to security things like user permissions and sql injection avoidance. The platform makes it horribly difficult to do those kinds of things in the traditional way, and really easy to do it in the consistent way.
So the rule really is that if it's difficult to write, and it looks like carp when you're done, then there is a proper way to do it.
It's what a friend of mine has been saying for years. It's not enough to make good code look good. You've got to make bad code look bad.
As I've explained many times at work, code is not documentation.
Code only tells you what it does. It doesn't say why it was done that way...and the "why is it doing this" is really almost always the problem.
They are just as serious. Updating/"maintaining" a piece of code and not updating the comment
should be made into close to a firing offense, after the suitable number of admonitions.
The biggest weakness I find with code I review is that the programmers seem to be either
inarticulate, lazy, smug, or exhibiting Aspergers syndrome (lack of empathy), in that they forget to
include the most important comment for any method or class;
The "What the h*ll is this?" comment that explains the gist of the method/class and why and
in what contexts one should care about it.
Also frequently missing is the considerate: "Mind the low headroom" comment.
Where are we going and why are we in a handbasket?
Except that one's "clever" is another one's "perfectly normal". For example, a client I worked for, had a huge body of C-code, where they — following one of the company founders' coding practices — would bzero a just calloc-ed array. In hundreds of places throughout the code...
Was it clever of me to not do it, knowing that calloc returns zeroed-out memory (its only difference with malloc in these days of flat memory), or was it stupid of them to insist on doing it for the sake of "reliable and maintainable" code?
In Soviet Washington the swamp drains you.
One company I worked at, when we sat down to discuss basic coding standards at the start of the project one of the developers whined and whined and wouldn't give up on one very stupid point. He was in his 60s and he claimed that he had used 2 spaces instead of 4 his whole life because it fit better on dot matrix printer paper. We pointed out that 2 spaces looks less readable and 4 looked fine on todays printers and fonts. He wouldn't give up and it was prolonging the start of the project indefinitely to the point where management said fine just use 2 then. So, we were all stuck using 2 spaces, which led to a giant war during development. We were all using Eclipse, so anytime he would write a piece of code with 2 spaces, the rest of us would would click Format document and cause all his 2 spaces to turn to 4 automatically and then re-commit. Which would make him cry, while the rest of us laughed. Essentially, majority won, but thank god for auto-format otherwise we wouldn't have been able to stay sane at that job.
Therefore, it is my opinion, that coding standards are crap. And, the only coding standard you need is to abide by whatever auto-formatting that Eclipse or your preferred IDE provides. As far as working out concepts like modularity and that sort of thing, simply refer to refactoring rules in design patterns and you'll do great. Enough said, now get coding and stop wasting time discussing nonsense.
While coding style is certainly important, the most clearly written, nicely commented, richly documented source possible can't tell you if the code does what it's intended to. Formal coding standards may help mitigate the difficulty of debugging and modifying a program, but I think can you get a lot more bang for your buck by implementing a solid testing framework. If I had to pick between coding behind someone with great style, or someone with great unit tests, I'd take the latter.
Meaning is very problematic beast. Philosophers has know already for few centuries how difficult it is to define proper names and point how meaning is bound to words or other constructs. If I have understood some what modern day consensus of philosophers any language will always be more or less defined for small audiences. To say that in practical way tech talk is talked by techies to techies. So called general language that can be heard in news or such will not cut, it just lacks practical way of saying difficult things e.g. news for nerds are not written same way as CNN articles.
Where does that lead us? Code documentation, style and even variable names can be meaningful and sensible activity, but only within limits of programming culture. It is waste of time to teach non programmers to understand 'this particular' code. Non programmers should first learn programming culture and then they should be able to understand. I understand many of you will say that there is no such thing as homogenous programming culture. In that you are absolutely right, and wrong. For example intending code is better than not intending, but we can argue till end of time is correct intend 4 or 8 chars. Programming languages have idiomatic expressions, one should always favor them. This sort of 'rules' are part of culture. But culture is greater construct than set or rules. In order to understand variable names for example one has to take a look of hackers dictionary (foo, bar, zyxxy...). Some times there are references to geek popular culture (I've seen magical number 42 too often that it would be just a coincident).
Defining coding practices is a very difficult task. The definition should match with culture and limit possibilities provided by culture. Paradoxes are hard to crack.
Elegant and clever are not the same thing.
Elegant describes the algorithm or solution as using resources efficiently, having no unnecessary steps in getting to the solution, and easily readable to another programmer familiar with the subject at hand (as AB3A mentioned inre to DSP).
Yes, if we use Webster's definition, there is a certain amount of "cleverness" in beautiful, elegant code. However, when we say "clever" we usually mean cutesy-clever, like mashing several traditional lines of code into a one-liner. That's fine for a geeky contest of "who can write this using the fewest number of characters," but (as most of us agree) does not have a place in professional code. One-liners and similar stunts might be clever, but are rarely elegant, and cause headaches for later maintainers of the code.
After you've been in this business for more than 20 years without giving it up to become a manager or guru, you may find that coding practices are orthogonal to code quality.
Just do the best you can with the cards you've been given.
I worked on some code where there was a comment from a few revisions before. I believe people who comment their code usually create better code then the people who don't comment or who don't update comments. The code and the comment were out of sync and now there was a problem. After reading the old comments I realized that whoever updated the code didn't understand the general design the original author had intended. I changed the code to correspond to the comment and the bug was fixed. The old comment saved me a headache. I comment regardless of "official" practices because I know months or years from now I might be the person trying to fix some code I wrote. With the turnover in software and software teams these days people don't believe they'll have to fix their own code and hence think they'll be able to keep everything in their mind until the next job. Maybe it's to save time but the same practice of typing less would be better practiced by taking your hands off the keyboard for a second and thinking about writing maintainable code. If people are going to cut and paste then change a few things to make it quite clear what they should cut and paste. If you are doing some boilerplate stuff that you know people try to turn into some generic thing change the variable names in all the versions of your boilerplate stuff. They won't notice the code is very similar and they'll actually take a second to understand the basic boilerplate design instead of cutting and pasting it. Push the sophisticated stuff in a super class and put the simpler stuff in subclasses. People who create a subclass at a drop of a hat won't be creating a mess that is hard to fix. Enforce conventions by putting in code that does. Put in a comment that says the code will explode without it. People who understand will laugh at the comment and the ones who don't know better won't dare take the code out. Make it easy to do the right thing and make it hard to do the wrong thing. Sounds like a nasty bunch of tricks but I've gotten numerous comments about how easy my code is to maintain. Too many and if I wasn't fighting managers to help "fix someone's code" I probably wouldn't be writing new code.
1k of code is simple in general to maintain. It probably has less bugs than 2k of code. It follows then that 999 bytes of code is simpler and probably has less probably of bugs than 1k of code. If you work with the logic, you arrive at the fact that if you write no code that is the simplest and most bug free version of anything you'll ever have.
Have you ever heard of a zero-day exploit to a peace of software that hasn't been written? Didn't think so!
Software development is really the domain of Zen monks.
One of guidelines I have found useful is that document interfaces as much and detailed as possible, but if you feel you should write a comment in implementation think twice if the implementation is too complicated, or maybe there is some problem with the design. Does the function do straightforward well-defined task? If the function gets too long or complicated maybe there are some independent well-defined tasks that should be own functions instead of part in the implementation. Write so clean code that it doesn't shout need for any documentation. One other useful guideline: Learn from bugs and improve code quality from the bugs. Always when encountering a bug, not only fix the bug but also ask "How could I prevented this in the first place?". That gives important hints about the quality issues in the code. Maybe you are not checking your assumptions/pre-conditions/post-conditions/variants enough. Maybe interface of that function was not well-defined. Maybe there was a test case missing. Maybe something else was wrong. Study the bug and make sure similar situation does not happen again.
A "hotshot" programmer will aggressively abstract. A mediocre programmer -- one who keeps his nose to the grindstone and is ever mindful of deadlines -- is more likely to "inline" constants (i.e. not abstract them at all) or even repetitive blocks of code. When ccde and constants are "inline", code is much easier to read because you're not constantly looking up in other files what the abstractions are.
Supposedly, both abstraction and locality aid maintenance.
The truly master programmer balances abstraction with locality. And takes into account who will be maintaining and the expected lifespand of the code. If the code is to be retired in two years, why bother abstracting? The general rule of thumb is that abstracted code has to be utilized six times in order to make the investment worthwhile.
When it comes to abstraction, the master programmer abides by the words of the Federation President, "Let us redefine progress to mean that just because we can do a thing, it does not necessarily mean we must do that thing."
Exactly! Mod this please. Anyone who has any knowledge of pointers will look at that loop and know what it is doing right away. The only exception is if you never learned pointers and just used the library function calls. That's what you get when you initially teach people such an abstracted high-level language (e.g., mega overhead JAVA). If you aren't going to use huge lists, there is no reason to include the overhead of the STL library (or any other); just form your own linked list (as done in the example)! Saves RAM, CPU and typing time. Also, it's 100% easier to prove correct mathematically (on paper). Try doing that with verbose library calls!
...manageable practices that ultimately offer a lot of value to the next programmer down the line who will have to maintain the code.
Simple: Use Python ;)
*ducks*
I've proven to my clients, time-after-time, for more than half a century, that programming is done BACKWARDS from good practice: We write code (maybe with comments), then we go back and document it.
Instead of some vague specification, we need to start with DOCUMENTATION. We need to write the first draft of the user manual (or, in modular programming, the programmer's reference) BEFORE a line of code is written. That document must be reviewed and amended until all (or a good representative sample of) the "consumers" of that software are satisfied with how it will work. (For example, Windows is NEVER reviewed by real end users in simulation or documentation, so we get the persistent and buggy output of Redmond's programmers.) AFTER we know what we're trying to achieve, we can write requisite code to achieve those results.
Yes, I know that's heresy...but it's the ENGINEERING way to do the job.
NASA, I'm sure, writes many things that must run in real time. Unfortunately, STL is not always a good thing to use in this scenario. My company actually has a coding standard to never use STL in code that is run in real time. So why is that? Let's say you're doing a vector.clear() operation. In the most common linux implementations, the .clear() function does not deallocate memory. However, that behavior is not in the C++ STL standard. If I recall correctly, only the interface is defined. What's going on underneath is omitted from the standard. So here's an issue we had in one of our real time systems. We were running VxWorks and a certain process was taking significantly longer on VxWorks than it did in linux. The reason why is because the .clear() function in VxWorks deallocates memory, which was not expected due to how it works in linux. So now you have a bug that took several months to find because no one felt they needed to dig into the STL code for VxWorks. And all because the coder wanted to simplify his or her writing process at the expense of knowing exactly what the code would do in all situations.
And as such...you are gonna get the "clever" no matter what practices you write down.
That is bad code, or at least, hideously idiomatic.
The variable names are apparently chosen for conciseness over legibility. The choice of 'ss' as both the loop variable and as an important field name appears deliberately confusing, especially when combined with its terseness. And the termination condition relies on the fact that a null pointer is treated as false in a boolean context - this is a non-obvious and largely arbitrary design choice in the language, and is arguably a type error.
I've been in this game long enough to recognise a linked list traversal when I see one, but I still had to read it twice to be sure. If it had read "for (currentNode = list->firstNode; currentNode != NULL; currentNode = currentNode->nextNode)" I'd have saved valuable milliseconds. If it had gone all OO and used some iterator object in a while loop, instead of that horrible for construct, even better.
Idiom, what the OP lacks is the common idiom in the language. Like the traversing a linked list as you have pointed out.
However, the code can improved by better naming of variables. The sin of the original code is to have the loop name be the same as a field in the struct, e.g. I think the following is more readable.
for (it=s->next; it; thisThing=it->next);
The first example that the author sites as an example of poor programming is "idiomatic" C for traversing a linked list. Also, if the author can't deal with pointers, he should not only avoid C, but also C++ and stick to Java or his beloved PHP and JavaScript. Siting that he found a standards document so flugly as GNU "helpful," doesn't help his argument.
First, learn C as C. Don't be scared, it's just like this "whole nother language" that's different from JavaScript. It can be used quite safely. If it couldn't, your beloved operating system of choice, that is likely written in C, just wouldn't work at all.
Second, draw your learning of the language not from standards documents, but by immersion in books like "The Practice of Programming." You will then find out that code like "for(ss = s->ss; ss; ss = ss->ss);" can be used without fear of misunderstanding because it is "idiomatic." People that know C will fully understand what it is doing. Writing a comment here is pointless and will degrade the readability of the code.
Third, add one further standards document to the list:
http://lxr.linux.no/#linux+v2.6.32/Documentation/CodingStyle
And learn that Coding standards are only as effective as those that use them.
C is a compiled language, so using long variable names has no effect on the amount of memory used at run-time
It does if you're embedding symbolic debugging information in that same binary - and running it on a system where the entire executable is loaded into memory at start, rather than paging in sections as needed.
In some cases, it makes the vagueness problem worse, because a programmer namedSomethingDescriptivelyButIncomepletely or namedSomethingElseSomethingThatItIsNoLongerUsedFor, or JustNamedItPoorly. True, these are programmer errors, but that doesn't mean they go away just with the magic of long variables. At least with variables like 'erating', you have a signal that you should pay attention. Which of the super long variables in this legacy codebase you're coming up to speed on are correctly descriptive and which aren't?
And don't get me started on halfassedly refactored Hungarian notation...
"Well, use long variables perfectly" isn't a viable response, because it will never happen all the time.
As far using autocomplete, I've tried to make peace with it. It just doesn't really work for me. Breaking the mental flow to futz with tab expansion or popup menus of options, well, is a break from the mental flow, and causes me to fall into futz with the UI mode, which is is a transition. Not a huge one, but it is annoying and non-helpful. I can work in noisy environments, but I don't like it, and it effects productivity. IDE "helpfulness" like this falls into the noise category, at least for me.
I forget what 8 was for.
We address this matter with a fairly rigorous team-wide coding style (which is easier to impart because we do pair programming, which is a great way to expose new developers to the team's practices and the product's quirks) and a massive battery of unit tests. These tests effectively serve as the basic low-level documentation of how something is supposed to work. There are a few comments here and there (to label the unit tests, or if there's a particularly tricky / confusing / subtle line or behavior). The tests say 'what', the code says 'how', and the comments say 'why'. Even the best comments don't prevent you from writing broken code. A good set of unit tests does.
We also wax a bit functional in our programming. You do need a modicum of competence to understand and write something like
but once you have that, it's a lot easier to understand (and harder to mess up) than a series of loops that try to do the same thing. Functional programming is a good thing when you can do it: the matter becomes less of what the code does and more about what the code is.
The World Wide Web is dying. Soon, we shall have only the Internet.
It took me 20 seconds to understand the code and it's over 9 year since I've programmed in C/C++. The "problem" is not the clever code but the new "clever" programmers.
You make some good points but
for(ss = s->ss; ss; ss = ss->ss)
could be better written
for(p = s->next; p; p = p->next)
FTFA:
As for the rest, the blockquote tags are messed up ... but I'm sure an einstein like you can figure that out yourself.
Blame slashdot's crap UI and lack of editing capability. Other discussion boards allow for editing/correcting, why not this one?
What do you mean "Approximately X"? The compiler and the computer do "Exactly X"... where X is what you tell it do to.
Are you suggesting there are languages that take my code and then instead of doing exactly what I wrote, they only kinda do what I wrote?
Really, I'm not sure what you mean. You seem to be operating and multiple levels. Are you talking programming languages, overall architecture, what?
Other than about the pointers, I tend to agree with the author rather than you on the points you mention. Which is fine, I suppose; we don't all need to program the same way, and it's probably good that we don't.
But my thinking is that longer, more descriptive variable names are simply part of documentation; it can eliminate the need to look through the code and find the comment tied to the declaration. (Although I personally dislike the 'initial lower case, capitalize all subsequent words' standard.) As far as skipping the brackets on single line blocks after conditionals... I think that is ALWAYS a bad idea. It's a pointless, inconsistent shortcut that can cause a number of different mistakes, just for the convenience of saving two characters. Yes, you can learn to always watch for that; but that's a learned response to a problem that doesn't need to exist. Better to avoid the problem in the first place. Adding the brackets never hurts anything, it increases the consistency of your formatting, and reduces potential mistakes... that's exactly the sort of thing that belongs in a standard.
I got a more modern example as well. The 'number one financial terminal supplier' makes use of enormous executables, basically all the different screens linked together into a single program. Eventually the executable size (with debugging information) hit 2GB and would not run on a certain UNIX operating system... they had to reduce variable name length whilst waiting for an emergency kernel patch.
Hmmm, sorry to disagree with you but if you were working in my shop and you wrote something like that, a warning of "That's enough of that shit" would be immediate. Repeating that behavior would result in an invitation to go practice your "craft" elsewhere.
As many many others have pointed out in this and other articles on the same subject there is little to be gained by this type of nonsense and in many cases much to be lost. An as another poster pointed out in a reply to your post, the reliance on a null pointer being treated as boolean is both risky and stupid since form one compiler implementation to another that evaluation is subject to change. even if you wanted to write this statement this way then to make it at least a proper statement then it should be: "for(ss = s->ss; ss!=null; ss = ss->ss);" ss != null is guaranteed to evaluate as boolean ss simply being null is not.
Hey KID! Yeah you, get the fuck off my lawn!
I worked for Electronic Arts in the '90's where nothing was documented. Not the code, not the tools, not the processes. During the final month before release, there would be a daily build and some teenagers would beta-test but there no other QA, no doc's, no reviews. Oh, they did have the "Wall of Shame" as I called it where bugs were assigned to specific coders. Great moral booster.
One tool I was required to use, specified all its time units in 1/100th's of a second. Another expressed amplitudes in dB mapped onto 0...75. None of this was commented or documented anywhere. You just had to unit test everything you touched to grok it's behaviour.
Needless to say, it drove me mad, espec. because I couldn't figure out why people were so resistant to change. All the programmers would just skunk away in the cubbies for weeks at a time, and keep the code to themselves like a shameful personal secret.
In the end, I figured the secrecy and lack of doc's was due to the company's vicious turnover rate; people figured they were less likely to be fired if they were the sole knowers of something. Also, no-one really wanted to help anyone else -- it was a very dog-eat-dog environment, with office politics trumping everything else.
From EA's point of view, I think it was actually cheaper in the end to have 20-something clueless coders churning out throwaway versions than to hire savvier, more experienced programmers. The buggy version would be replaced by next year's buggy version anyway. And by creating a paranoid work atmosphere they also managed to suck a lot of unpaid overtime out of people.
My suspicion after 20 years of professional life is EA is not exceptional. Academics and programmers tend to agree on the need to document, test and verify, but in the business world the reality is that quick-n-dirty is cheaper. I've read all the arguments that debugging is more costly than coding, etc. but it's just not borne out by practice.
i worked on fortranIV code that had been migrated from fortranII that had been migrated from whatever language was used on an ibm drum-memory machine;-) there were variables named for drum memory locations, 4col...i wasn't about to change those;-)
and those codes were written by the resident gas properties genius, who was still on the job;-) it was then i realized that the clarity of code is inversely proportional to the brilliance of the coder;-)
Yes, you are right. It is Slashdot's fault that you didn't preview before you posted, and it's the compilers fault when you don't #include the header file that resolves external references.
You just run willy nilly with all the rules of writing, now don't you ;-)
Guns don't kill people; Physics kills people! - John Lithgow as Dick Solomon on Third Rock From The Sun
... in any _useful_ fashion. Notice that semicolon at the end of the line after the closing bracket?
D'oh!
When I first started using C, a lot of compilers only recognized the first 8 characters in an identifier name. Also, the CBM basic interpreter only recognized the first two characters (you could use more, but they would be ignored. I am sure I was not the only one who responded to this by only using 1 and 2 character length variables. Not saying it was a good idea, I'm just saying I did it). So a C program with only two character variable names doesn't seem unlikely to me.
Agreed on your second point. Some coding tasks are complex, and require complex code. If he gets confused by linked list traversal and alternate bracing styles.........may he never look at networking code. It will kill him.
Qxe4
A good autocomplete implementation works just like tab completion in a good shell. Type as little of something as you need and hit tab.
Both Visual Studio and Eclipse auto complete seems to almost work as good as a tab completion in bash. A lot of the text editors I use usually have shitty implementations that pop up so soon they really dont even know what I'm trying to type. Honestly, I cannot even explain why the auto-complete in VS just works, nor can I explain why autocomplete in my text editor sucks--my fingers and brain both get along with one and fight the other.
I also wonder if those who abhor auto-complete are also people who are hunt & peck typists. If you are busy looking at the keyboard while you are typing, then any kind of auto-completion would be annoying. You might be typing away and not notice the focus has shifted from the code to some list.
Is there any C/C++ compiler in existence where NULL is not a boolean false ?
The proponents of the two sides of this "argument" seem to me to be artists and constructionists. In software, art is what happens at a hacker conference or at a startup BEFORE the investors start poking around. Construction (including architecture and source control and coding standards and design patterns et. al.) is what happens at a medium to large company that takes huge amounts of skilled workers to build something that can then be charged for in copious amounts and which will then make investors rich. In short, hacking vs. coding. Both have a place in the world. Neither should be influenced by the other. The big problem with software is that the former usually morphs into the later rather than being a completely separate effort which should include none of the original source material and none of the original players. And finally, a good hacker is NEVER a good coder, and a bad coder is often NOT a gifted hacker, but someone who shouldn't be doing either type of work.
He makes some good points about naming symbols, but: the guy does not recognize a for loop scanning a linked list (and even think it is a "clever" trick) and does not know that he can automatically indent his code under vim.
He is a total joke. He should look for an other job.
My idea, in answer to your question, would be modernized Literate Programming combined with outlining.
Traditional-style lit.prog. is done in flat files, and one has to keep the structure in one's memory.
Combination of lit.prog with outlining or folding will relieve short-term memory, eliminate switching between "housekeeping" and navigating and programming, and will scale literate programs.
Secondly, traditional lit prog erects an unsurmountable PSYCHOLOGICAL BLOCK before a programmer demanding that he produced a "polished essay" of a program.
Lit Prog must be used as PROGRAMMING FROM SPECIFICATIONS IN HUMAN language, as programming in pseudocode which becomes precise new meta-operators, and be free from the demand of nice exposition.
Rather, if practised as a tool to help keep notes, ideas, lists, references alongside your code snippets, it becomes an accelerator, rather than a drag on your feet.
And thirdly, lit prog must be - on 90% of uses, which are by practicing programmers rather than paper-producing academics - rid of its dependence on TeX or LaTeX.
The most ubiquitous markup today is HTML.
I wrote a script which implements these ideas, and called it Molly for "MO-dule for LI-terate programming":
http://github.com/unixtechie/Literate-Molly
He uses this code "for(ss = s->ss; ss; ss = ss->ss);" as an example of bad code: "For those of you that are interested, the line traverses a linked-list of sources and sub-sources to process the values inside. But it took a good deal of research to figure that out, because there were no comments and the variable names, well, suck." Well, I read that and said to myself, "It's traversing a linked list."
If you see the pattern yes, but the naming is just horrible. "for(ss = s->first; ss; ss = ss->next)" would be quite easily understandable. Plus it'd match the typical first/last next/prev way of naming usually done in a doubly linked list. I would not immediately understand that, particularly not if it wasn't obvious from the function that it would be looping through anything.
I think everyone should be forced to do what I did when I programmed on my TI-82 calculator, it had no autocompletion and variable names were painful to repeat so they became single letters. I could do quite advanced stuff as long as I kept working on it and had it fresh in memory. Tried looking at it later, it was a complete clusterfuck to dechipher.
This guy looks lof the same caliber, s->ss and ss->ss as member variables? I mean in a function you can use almost any lame naming and people should be able to figure it out because the whole purpose of the variable is right there in the function, but if it's not clear what the members are you have to dig through the whole class and find when, why and how it's being set and/or used.
I don't really mind pointers, but they have a way of adding a very confusing layer of indirection that often makes it unnecessarily complicated to understand what's doing on.
Live today, because you never know what tomorrow brings
I love how his example uses the most confusing variable names possible. Just using proper names should make it clear to almost anyone:
You've answered your own question: an elegant one-liner coupled with a comment from a few revisions ago makes for a good headache.
Clever code and maintainable code are not antagonistic. Clever code that's well commented is as maintainable as the more pedestrian type. There are two problems.
The first is that the second version is often an awful hack by a programmer, not the one who wrote the original code, who doesn't fully understand what the code is doing. There is no law, not even a best practice, that says all code, even well-commented code, must be comprehensible to someone with a two-digit IQ.
The second problem is getting said programmer to comment what he did. I suspect in many cases he doesn't know, except that the immediate symptoms have gone away. I call this a goat's blood and chicken feathers patch. Unintended consequences will be someone else's problem.
Maintainability is only one of many important quality attributes. I've written more 'clever' bits of code than I care to count, usually because the alternative would result in failing some other measure--response time, size, reliability, you name it. In every one of those cases the comments provided a technical manual for the subject code.
There's one case I won't forget where 14 lines of assembler was supported with 40 lines of comment, not including the comments on each line. I was invoking little-used and poorly documented features of the cpu chip and using them in an interesting way, and an explanation was appropriate. This code still runs fine on a modern PC. (If anyone is interested in a really fast x86 triple precision integer division that handles the signs of the quotient and remainder according to the Forth standard, I've got one.)
Clever code isn't the problem; poor documentation by poor programmers is the problem.
I'm a Programmer. That's one level above Software Engineer and one level below Engineer.
If you build obvious enough code, you only need a few comments, such as "this business thing that does so-and-so starts here/ends here". All the comments in the world don't make a "clever" solution any less douche-y.
And this is relevant to 99.9% of the software out there how?
"Facts" like this are what turn into programmer urban legends that haunt us forever. "Facts" like it is better to manually inline your code rather than let the compiler do it. "Facts" like if you are just copying a string you should do the pointer arithmetic yourself rather then "wasting space" pulling in the standard library for strncpy. "Facts" like with .NET you should always set variables to "null" to save memory. All rumors. All urban legends. All passed around like they are the truth.
When you make statements like yours, make sure to add *huge* guard bars. You comment only applies to a very, very narrow range of programs in the wild. In fact, if you worked on such a system you'd know this applies to you automatically.
In other words, your comment is garbage--write descriptive variable names and don't fucking worry about "saving memory" or "saving disk space" unless you are an complete idiot. If you are working on a system were long variables eat into your 10k of flash memory, then we can talk--though I'd question why you are embedding such detailed debug info in your device in the first place.
No. The language requires that Null is always zero. It also requires zero to always evaluate to false and anything non-zero to always evaluate to true.
It takes me one second or less to recognize that "for(p = s->next; p; p = p->next)" is iterating a linked list, or a close relative thereof. It definitely takes several seconds for me to recognize "for(ss = s->ss; ss; ss = ss->ss)" as doing the same. At first glance it looks like the initial and loop conditions are the same, thanks to using "s" and "ss".
I'm also not particularly used to seeing a for loop being used for linked list iteration. I'm familiar with for_each loops for the purpose, in languages that have such a feature, otherwise the code I see tends to use while loops for this. The for loop form is actually better in that experienced programmers can quickly recognize it by the structure of a single line, while the while loop requires scanning multiple lines.
This is not nearly as bad as say Duff's Device which would be very confusing upon seeing it for the first time.
Stylish sheet to fix many problems in Slashdot's D3: https://gist.github.com/801524
for(ss = s->ss; ss; ss = ss->ss);
The semicolon at the end means the loop is only "processing" in the middle statement. Testing to see if ss is NULL does not processing make. Either he took liberties and left out the loop payload, or he still missed something and still doesn't understand it. It is true that in an embedded system, merely reading an address can have side effects, for example reading a register can cause hardware to perform actions, etc., and maybe that is what is what is going on here, but I doubt that is the case here since the addresses would have to be pointers to other registers and registers themselves at the same time (though it is still conceivable.)
I believe he meant to write: for(ss = s->ss; ss; ss = ss->ss) { [some kind of processing here] };, but that is not what was written, which is ironic in an article complaining about unclear writing of any kind.
I agree that the variable names aren't good for code written today, but C was written in the late 1960s when core (i.e. memory of the day) was expensive, and NASA has been around that long of course, so maybe at the time the code was written it was good code (and yes I know the size of the executable won't be bigger, but the size of the source will be, especially if s and ss are used throughout a large program, and source code lives in memory when being compiled too.)
He certainly shouldn't have needed more than a few seconds to figure out that the loop traverses a linked list.
Guns don't kill people; Physics kills people! - John Lithgow as Dick Solomon on Third Rock From The Sun
I agree with you. Open Source is a better paradigm ;-)
Guns don't kill people; Physics kills people! - John Lithgow as Dick Solomon on Third Rock From The Sun
If naming (variable, function, class, module, whatever) isn't part of your coding standard, you're going to have problems.
And pretty much everything else is just an optional "nice to have" (usually to do with spacing).
I am the asshole with the 40-character variable names. Yes, I need to split statements over multiple lines lines sometimes, yes I have more lines of code and it takes me longer to type them, but I think it's very worth it to be able to pick something up months or years later and instantly be able to tell what the hell everything means.
As for spacing, I would absolutely love to format all code very strictly, and reject any commits which don't match an automated formatter exactly (for XML, this is possible, of course), but I've yet to see one which doesn't have really horrible edge-cases involving comments.
-- 'The' Lord and Master Bitman On High, Master Of All
...the guy who is going to maintain it is a 6'6" bodybuilding psychotic maniac who knows where you live.
My opinion? See above.
We deal with this by using something that can be described as code editors.
These are people that edit raw code committed by other programmers and refactor it in order to comply with a set of standards. Editing may entail just changing a few variable names, but it can also be almost a complete rewrite of the code. Of course, if code rewrites become the rule rather than the exception, something is wrong and some serious talks with the original programmer will be necessary.
As long as the code editors are knowledgeable people and agree among themselves about the specific style, best practices and architecture to be used, this can greatly improve the quality and consistency of the code.
The original article referred to the program being developed on a very old system and the comment to which I replied inferred that the chosen short variable names had no benefit. I was pointing out why the code he's looking at now might be the way it is, not suggesting everybody writes code today with single letter variable names.
Get back to your fucking visual basic programming and leave me to my $400,000 + ~40% bonus lead financial programmer role.
Right, and seatbelts are useless because people often don't use them, or they fail to buckle them properly.
Guns don't kill people; Physics kills people! - John Lithgow as Dick Solomon on Third Rock From The Sun
I would think that correcting Knuth on programming practice has got to be way up there.
You might also consider telling the computer to use some line breaks when you separate your paragraphs, since the aim of slashdot is ostensibly to communicate with other people.
does the trick.
If I have seen further it is by stealing the Intellectual Property of giants.
I like to use ternary conditions in my code where strings output based on a condional
for example
int n; //number of shoes
printf("we have %d pair%s of shoe%s",n,n ? : "s" : "",? "es" : "");
If you aren't used to it it is confusing, but if you are it saves a ton of code.
Is it considered a clever one liner?
How about // do loop until return or break
while(1)
{
}
My code is usually very terse, which helps me maintain it because dense code requires less reading to understand. (IMHO)
You just might get it.
I have seen the opposite problem crop up: an institution so focused on creating code according to standards that they actually make the situation worse:
So, be careful what you ask for, you might just get it. Generally speaking, it's easier to convince a sloppy programmer to write better code than it is to justify a deviation from already-established, albeit improper, coding standards.
The society for a thought-free internet welcomes you.
So you don't put up with "shit" like using widespread idioms and relying on standard language features? Sounds like a lovely place to work.
Knuth is not suggesting one should be incorrect for the sake of maintainability, understandability, or anything else. In programming, if a solution is possible, there are infinitely many solutions, a few of them are easier to understand than most of them, and understanding is the key to verification and so to the writing of correct code.
NULL is always 0 or (void*)0.
The second method is commonly used on machines that have pointers of different lengths (For example, some crays have char* and int* of different lengths - they address completely different memory segments indeed)
When you use 0 in a pointer context, the compiler replaces the 0 with a special bit-pattern, the "null pointer representation".
On most modern machines this is also 0, but not necessarily. The bit-pattern can also be different for different pointer types (again on a cray,
a char* null pointer representation is different from an int* null pointer representation)
char* a = 0;
On most modern machines, this will set 'a' to 0, but it could set it to '0xffffffff' on a cray.
if (a) {
The compiler will convert to something like
IF A != NULL_POINTER_REPRESENTATION
So, on a modern machine...
IF A != 0
On a cray...
IF A != 0xffffffff
That's why code such as
char* a = 0
if ((int)a) {
}
is not strictly portable. On a cray, the comparison would result in "false"
That's why if you call a UNIX function such as execl(), which take a variable number of arguments, which /"etc", 0);
are terminated by a char* NULL pointer you have to do
execl("/bin/ls", "/etc", (char*)0)
and NOT
execl("/bin/ls",
because otherwise the compiler will not know to replace the last argument with the null ptr representation, which may not be 0.
A computer uses machine code, so why should we use higher-level languages? The reason is so they can be better understood by humans -- firstly and most importantly, by the author herself. Correct code can be written more quickly, and errors more readily detected, when using languages that approximate to natural (human) language, and Knuth is simply proposing that we go further in that direction.
A suite of unit tests document exactly the software specs for each software unit. It really improves modularity.
Unit tests usually test for ambiguities and edge cases, and report back when results are not as expected. I've found that when programmers write unit tests, their code has far fewer stupid errors, just because they have to think about edge cases and boundary cases in designing the unit test. When finding unit interaction bugs, the unit tests make finding errors much easier... you already know the behaviour of a routine, and if there is ambiguous or incorrect behavior, it is time to extend the unit test.
The extra time in writing unit tests is well worth it if the end software needs to be reliable or is going to be maintained over time. Total no-brainer from the organizational viewpoint. From the individual software developer's viewpoint, it's not so clear if the unit tests are worth it, especially if you probably won't be the one maintaining it. It's usually easier to write it without any tests, probably save about 30-50% of the time coding, use some of the time savings to fix any glaring bugs that crop up, and leave/blame the inevitable unobvious bugs to your successors. Or charge the client/company more to fix them.
it's interesting that like many things, those least qualified
to evaluate code, have the strongest opinions.
Include ASCII graphics and cursor control characters in the comments so the code is unreadable.
Use comments to record what the voices in your head and/or pants are saying.
Make jungle noises at staff meetings before answering your shoe then running out.
They will make you CTO.
John McAfee 'It was like that time I hired that Bangkok prostitute; to do my taxes, while I fucked my accountant'
The code mentioned on the linked page:
for(ss = s->ss; ss; ss = ss->ss)
I'm sorry, but that's obviously moving through a linked list untill the end (NULL == false) pointer.
Words lead to sentences
sentences to paragraphs
paragrpaphs to books.
books make you suffer.
Suffering bad.
Well - one of the reasons we use Python. I'm sure some people will consider this a troll, but it's true. Some languages encourage readability, some don't. You can write horrible, awful, unreadable Python and you can write elegant, readable, maintainable Perl (Calcium was the best I've seen in a real product), but neither is encouraged and you know what you tend to get out of your programmers.
This means our code takes a bit longer to write up front than just banging it out in Ruby, but has the advantage that when we come back to it six months later it's still obvious what's going on (even if someone else wrote it) and it's easy to maintain.
Ruby's great (and a close match for Python in niche and functionality, which is why I'm using it specifically), but our Ruby guys can't do that because they love to make things as concise as possible - it allows them to show how clever they are. Perl-style magic variables and punctuation everywhere. They sometimes rewrite functionality from scratch because it's easier than deciphering their (own!) old stuff, which at least unit tests help with. On the other side, Python's indentation generally limits how much you can cram into one line without gross misuse of lambdas, which frees you from the need to even try it.
Perhaps this is self selection - maybe boring people like me who learned to value maintenance considerations (thanks to hard experience) gravitate towards easy maintenance languages, which exacerbates the effect. But I think still think your choice of language will strongly inform how maintainable your code will be; you can mandate code policies for any language, but how often (and for how long) are those actually enforced in practice?
90% of my work has been in the Canadian Oil and Gas industry and EVERY company that I've worked for has sought 'get it done' above 'do it right'. In two decades of programming both as a consultant and as an employee I've NEVER had a code review. Even during my two years of Y2K work, whatever I delivered was just fine as long as the end client was happy.
I've inherited systems where the documentation was literally four pieces of 3"x3" sticky notes that covered 'all I needed to know' about 28 in-production apps that I was now the sole supporter of. What was on those sticky notes? The user accounts and passwords for the Oracle database back-ends for the systems and the application names. As you can expect, in-code documentation was non-existent. When I attempted to improve the level of documentation for these systems (even just putting down the 'basics' of the system information on a single sheet of paper), I was threatened with dismissal for being too slow in working with these systems. The other developers who had to interface with these systems I now supported greatly appreciated this additional information as it empowered them and made their work faster, but only a few of them adopted the use of this single-page documentation solution for fear of pissing off the same management that had overseen the creation of this fiasco.
Perhaps I've just been unlucky in my career and I've only stumbled into positions where results always override process, but since I've seen it in small companies, large multinationals and in three levels of Canadian Government, so I suspect that any talk about documenting systems and truly following 'best practices' is just so much BS. What management wants is results ASAP. When things fail it isn't management's fault, yet it is management that prevents systems from being built with adequate documentation and review because that takes longer and costs more, for which they are penalized, and when systems fail, it is the programmer who is at fault, not the manager.
I figured that once I moved into management that I could become part of the solution, but then I found my VP giving me the same results pressure AND the developers who reported to me, ironically, simply wanted the code to be the documentation (The Agile 'the tests ARE the documentation' BS).
Nowadays I simply program for myself and for my direct clients, and this is the only way that I've found to be able to ensure that systems are adequately documented without encountering a fight or threats on my employment.
It is shorthand, and shorthand is contextual.
If suddenly in the middle of a module to calculate the trajectory to return to earth, there is a reference to an out of scope notion like the employees rating; then a more descriptive name is very appropriate.
If it is in a context which is doing computations about employee, department and company ratings, I would hope the person would be smart enough to use shorthand; otherwise all that annoyingly redundant crap just makes it more difficult to read and maintain.
Why use sin() when it could be "RatioOfLengthOfFarSideOfRightTriangleToHypotenuse()"?
How about "YCoordinateOfPointOnUnitCircleAtAngleFromOrigin()"?
Exceptional programmers write code. They can't--can not--be paid to maintain the code of other people. They leave. They find other opportunities where their prowess and ability to think in code is appreciated.
Mediocre programmers take any job they can get. They pore over the cryptic codex's of their thoughtful predecessors like acolytes transcribing the utterances of prophets. Their most important talent is that they stay.
Code maintenance, therefore, is a non-starter. Architects and Engineers build buildings, and plumbers maintain them. Plumbers can fix a leak, but it'll take a while and everything will have gotten wet. They certainly can't re-engineer a chilled water delivery system to meet new building code requirements or calculate the pipe required to handle superheated steam.
Extending the purpose or function of an application isn't "maintenance". It's re-design. It takes engineers, not plumbers.
There's no amount of dumbing down that stupid can't be baffled by, but harnessing your Arabians to oxcarts will guarantee that your software never ships in the first place.
I find it surprising that this isn't obvious to anyone who knows a decent programmer.
aka Matthew at SlashNOT/!
And this is relevant to 99.9% of the software out there how?
It isn't. Now. But if the code is inherited from the "olden days", then those stupid restrictions did matter back then. (I remember Ultrix. How I hated how Ultrix carried out linking. The old Linux a.out system was much nicer, despite being awful.)
"Little does he know, but there is no 'I' in 'Idiot'!"
There are huge differences between different algorithms, and picking the right one will usually sufficient without further optimization. For a very rough overview look at http://en.wikipedia.org/wiki/Big_O_notation.
So choose an algorithm with favorable "Big O", implement it cleanly and try if that is fast enough. Only if that is not sufficient, try clever tricks.
C - the footgun of programming languages
The article could have had better examples. That loop for walking a linked list was not hard to understand. Leaving out all the context was the only thing that made it even slightly obscure. Likely the author was spoiled by XML. 30 years ago, this guy would have been a COBOL programmer.
The sort of bad programming I've seen is orders of magnitude worse than anything the author was complaining about. How about code that relies on compiler bugs to compile correctly? Code that is correct, but doesn't work because someone got clever and elsewhere in the program decided to call on system libraries to change how the OS responds to some events, to "fix" something totally unrelated? Naturally, when you undo damage of that sort it will break something somewhere else. Another fun one was the initialization code that didn't work and no one realized it because that code path was almost never taken. All it was supposed to do was create a few directories and files, for temporary data, however the installation software also did that, thus masking the problem. Then a clean up wiped them out, and suddenly the main program was crashing. Actually, since the main program did successfully create the first missing directory before crashing, the solution was to keep invoking it until it made it through the initialization. About 7 invocations as I recall, to get all the directories and files recreated. No need to fix that, right, since it will eventually work!
Intellectual Property is a monopolistic, selfish, and defective concept. It is "tyranny over the mind of man"
Better yet.
for(p = s->next; p != 0; p = p->next)
Sure it's more verbose, but it's also more readable especially to a novice, since it's closer to the usual prototype of a for loop. (i =0; i xxx; i++)
These posts express my own personal views, not those of my employer
My customer is the next poor slob who has to work on the source code
Likely you. :)
In most work situations, chances are better than not that once you create or touch something, responsibility for its maintenance is going to stick to you as long as it can.
And right now I'm looking at some hobby code I wrote a year and a half ago. I was probably less careful than I should have been when I wrote it, because I knew the only reader/user in the near future was likely to be me. But "me" over a year later is quite possibly just about as clueless about what undocumented single-letter variables are and how a dozen functions fit together as a completely new initiate might be.
Tweet, tweet.
Well he's at +5 already, but he has it spot on.
When I talk to other programmers who can't see the value (or are lazy) in providing comments, I try to make it clear to them the comments are the RATIONALE for the piece of code. We know we can see a loop that iterates over a list, but why did Fred (who left before I started) write this module in the first place? He could have mentioned in a short blurb at the top that it updates a person's Mojo and that it's only used by the AustinPowers module, thus saving me time doing a global search on the thing. And given that I don't have any RATIONALE for Mojo.dll existing I sure as beans won't have any reference in AustinPowers saying the Mojo was moved into the YeahBaby common library because of some reason or other.
RATIONALE. The WHY of the code, as so neatly put by the op.
Any idiot saying clever doesn't pay the bills or clever is too difficult to maintain is simply admitting he's stupid. Grow some brains or get the fuck out of the industry. We do not want you, we never wanted you, and you are an infection.
This actually sounds like Ruby, which once you get past the powerful, yet seductive syntax, you can actually write things in a much more compact way, while retaining the readability. It doesn't go too far, like making the whole code like reading prose (too much text is also horrible), but mixes minimal syntactic sugar with text and good function/variable names, so that you can write iterators and whatever language extension you need, while retaining the original meaning of what you're overloading, or just use the common ones. Beats the hell out of for i = 1 to 100, although you can use that convention too as a stepping stone until you get the dynamic beauty of intepreted prototype-like languages like Ruby.
Most Ruby libraries are actually thus very easy to read, since the same syntactic sugar are used over and over, to save lines of code, and convey the similar or exactly the same meaning, without repeating itself unnessesarily. It's neat to have one way of iterating, instead of so many variations like:
for i=1 to 100 do
i=1;while i = 100) break;end
etc.. You can still do this in Ruby, but since all Ruby-libraries (which span everything you will ever need) follow ruby conventions like "duck" typing, you will eventually start using the more "rubyesqe" conventions more and more. They just make more sense and give more flexibility.
Literate programming sounds neat, but actually Ruby with its DRY and other principles has practical solutions for spagghetti code. It does require programmers not indoctrinated with Perl though, as they will just go into their old routine to make a mess (just kiddin, almost ;-)
http://www.debunkingskeptics.com/
What happens, then, when a single structure needs to be contained in multiple lists?
You might try to dismiss that as not a good idea, but I'm thinking in particular about some structures in the Linux kernel where this is the case...
I disagree about NULL being false as being a largely arbitrary design choice. To me it reads as
if (/* there is a */ thing)
doSomethingWith(thing);
To me, that's a hell of a lot clearer than
if (thing != NULL)
My thoughts exactly when seeing that code. It just didn't compile in my head, so have no other comment about the other stuff grandparent post tried to convey ;-)
Too much C / C++ in childhood.. Now it's Ruby all the way... ;-)
http://www.debunkingskeptics.com/
Which suggests that you're not a C programmer. Fair enough, but you were warned:
"The first sign of danger was the entire code base is written in C,"
What this section of the article really lacks is a description of the struct(ure)s that s and ss point to. Maybe this is a simple linked list, and 'next' would make sense. Maybe it's a tree of some sort. Maybe ss had a contextual meaning that the author obliterated in his attempt to make a point.
A cynic might point out that this is a self-confessed 'web development' author, but that would just degrade this post to his level.
As a sophomore CS student, that code took me 10 seconds to figure out as a linked list traversal.
I'm with the comment above: TFA author seems ignorant of pointers.
Use a modern editor that can store the strings of all previously used variables
Allow me to rephrase: How do I tell the editor how to find "the strings of all previously used variables" in code written in the assembly language for a given microcontroller, specifically one with a 6502-compatible CPU core? Two ACs recommended that I look into Emacs with an appropriate major mode, but it appears asm-mode supports only indentation, not autocompletion.
You assume wrong. I've had to correct code because people coming from the Windows world NEVER turn on warnings. Many don't even know how to edit a make file. If there's no clicky thingee, they're lost.
Anyone who cannot use make should not be writing c code. This is basic stuff. Then again, so is using ctags and fgrep.
Ditto anyone coming from java. It's NOT the same, the mindset isn't the same, the conventions aren't the same, and someone with 10 years of java experience is totally useless when it comes to designing in c or c++. They try to make everything look like java, and they break things. And they keep on whining about needing garbage collection or smart pointers or the whole stl because they can't even write a simple linked list.
If you can't write and manage a simple link list, a stack, a queue, and proper memory allocation in both c99 and c++, you should go back to java and leave c programming to c programmers. The guy who wrote the original article is a java programmer - look at the complaints and the references he provides.
I look at the language standard. Look at the if() statement. It is followed by one line, and a semicolon. No parenthesis. Parenthesis allow you to group multiple lines (blockify) - and its' always possible to replace any block of code with a one-liner (either a function call, a macro, or even using the comma operator).
Indent your code properly and you'll never have a problem with parenthesis - and your code will be more concise, thus easier to read.
Adding brackets increases the visual pollution, and I really hate it when people put the opening brace on its own line - its a waste of a line. Folding editors can handle braces at the end of the line just fine - why can't people? Concise can be clear as well.
Java is always interpreted - sure, at runtime, it does "just-in-time" compiling and caching of byte-code - but it has to INTERPRET that first. You can't keep the cache between program runs, for security reasons.
The only exception is native code - which isn't java.
The relevance to the article is that the article complains about how the code is wrong, but it looks fine to me, and I've been using c for decades. If you look at his "reference" section, you'll see he cites java style. Also, the idea that the stl is a solution is ludicrous in a c program. Ain't no such animal. And his example of a method name is also wrong, because (1) c doesn't have class methods, just functions (you can embed a pointer to a function in a struct, but it's not a "method"), and (2) you should be able, in a c++ program, to use the shorter method name because you get the context from the class name. That's the idea behind polymorthism - Man.paint() probably isn't the same as House.paint(). PolicyHolder.risk() isn't the same as CrossStreetBlindfolded.risk() isn't the same as RussianRoulette.risk(). You don't need to write PolicyHolder.riskOfPolicyHolderPayout(), CrossStreetBlindfolded.riskOfGetingHitByCarWhileCrossingStreetBlindfolded(), or RussianRoulette.riskPlayingRussianRoulette().
Java programmers should not criticize c programmers unless they're ready to get some serious pushback, that's all I'm saying. There are a lot of things that are done a certain way in c for a reason. Conventions have developed over the last few decades, and c system code doesn't look at all like c++ application code. You can safely assume a certain level of expertise in system code - if the person doesn't have it, it'll be apparent quickly because nothing they do will work.
It's like the engineering professor who proposed that the best test of their teaching ability would be to have the teachers take a ride in airplanes that their students designed. "That's no fair!!!" So finally, one of them asked "Would YOU get in a plane designed by one of your students?" "Of course. It would be the safest flight in the world - it would never get off the ground."
It's not cryptic to confound others - that's just a side effect, but it does raise the bar, which is a good thing.
Yes in fact shops I have run were lovely places to work. We wrote code that was both efficient AND readable, One day you might run a shop and when you have programmers of various skill levels having to follow some who wrote code like that you will appreciate what this whole argument is about.
ss->ss is horrible, it is indefensible, period.
In any linked list the previous node is always [struct]->prev and the next pointer is always [struct]->next. Those two things explain exactly what is going on, no confusion it is crystal clear to anyone reading the code.
Writing struct->next != null might take a few more keystrokes but is is crystal clear, leaves nothing to chance and on top of that it takes no more work for the RTL and I might be mistaken but I think it takes a few less instruction cycles. Although I have never bothered to look at the generated code from the compiler, my guess is that the end result is no different from using the idiom of var++ -v- var = var + 1.
Hey KID! Yeah you, get the fuck off my lawn!
ever heard of list_for_each, list_for_each_entry, list_for_each_entry_safe and the awesome linux/list.h?
The implementation details should be hidden in the function call. This way, risk() is free to either check a variable to see if the value has already been calculated, or do the calculation itself.
Example (sorry for the non-tabbed formatting): risk() {
current_risk != NULL ? current_risk : calcCurrentRisk();
}
This also lets you have other parts of the code invalidate current_risk, so the next time you call it, it updates the value. Doing the calculation every time can be a waste of time.
When I'm really stuck for an expressive variable or object or function or method name, I break out the thesaurus. I'd rather spend 10 minutes really thinking about what I'm trying to do, and get the right name, than pick the wrong one and have to "remember" that it's not exactly what it implies.
If you can't write and manage a simple link list, a stack, a queue, and proper memory allocation in both c99 and c++, you should go back to java and leave c programming to c programmers. The guy who wrote the original article is a java programmer - look at the complaints and the references he provides.
Any kind of programmer at all can do trivial tasks like a linked list, a stack and a queue. The simple fact is that if you are paid anything above minimum wage, you are absolutely wasting your time doing so when there are perfectly good libraries that do this sort of thing very well. This has some rare exceptions where none of the available libraries are appropriate, and the key word there is rare.
Okay, those are horror stories - and I can believe the "create multiple directories" one - and not just with directory creation. What's really bad if it doesn't create all the directories, doesn't notice, tries to change to the new directories, then goes on to create its' temp files ... and delete every file in the directory when done.
No, it wasn't me, but it could have been. Sometimes you're just too rushed to check everything.
In my opinion, if that for loop were only a few lines long, the naming would be acceptable. If the loop were 100 lines long, I'd ask for a more descriptive variable name. I use "for(i=0; i<something; i++)" all the time, if it is a very small loop and the usage is obvious.
As for...
The culture he's been inculcated in is one in which you never have to understand how a linked list is actually implemented, because you just use a library for it.
... I prefer to think that in OOP systems, we hide the implementation of the loop not to pretect our fragile brains, but to not bind the code that uses the list to a specific list implementation. I like being able to switch between ArrayList, HashTable, SortedList, List<Stuff>, and Dictionary without breaking my iteration code.
OK, dimbulb, what is this code supposed to do:
if ( test ) /* don't forget this! (see DR 1234) */
do_something();
do_something_else();
Familiar patterns - like ALWAYS using brackets - are NOT clutter. You need to get off your faux-sophisticated, misinformed, pedantic high horse and learn how the most powerful tool used to program computers - the human brain - actually works. And then learn to write your code in ways that work WITH your brain's natural abilities.
Also, you NEVER should use tabs instead of spaces for indentation. What happens if someone happens to use an editor that expands tabs to something different so that code no longer lines up?
You always use the same editor? With nice, controllable GUI interfaces?
Well, then you're nobody.
Because until you're the guy on call who has to be able to fix things over a satellite link with 500 ms RTT and no bandwidth, you won't know why you have to write code so it's consistent no matter what editor you view it in.
"hideously idomatic"
Actually, the construct is rather pleasant. I now know that if s is a pointer, ss is a traversing pointer, at least in a singly linked list context. Use of ss outside of a "for" or "while", and initialization from anything but an "s" should raise alarms.
So, I can infer what pp would mean, if I came across it.
All this from a SINGLE STATEMENT. Wow, to C coder, this IS reasonable code! Not so much to someone new at the game, though.
As to NULL being zero... that is simply a precondition of the language itself. This is C code, for gods sake! It's not Java. Idioms. Deal. I wonder what happens when these programmers see level 88 Cobol declarations, or FORTRAN equivalence statements, assigned goto, or arithmetic ifs, or even a recursive formulation in C! My, I am getting giddy thinking about the reaction.
On the other hand, this lack of experience is good for me. I just made some money modifying some COBOL CICS code. Because I can.
Now,
"for (currentNode = list->firstNode; currentNode != NULL; currentNode = currentNode->nextNode)"
does NOT mean
for (ss = s->ss; ss; ss = ss->ss)
At least, not until you add
#define firstNode nextNode
into your program. Which WOULD raise a WTF!? moment for a lot of programmers. Also, The Fine Article also recommends using STL. In C code. Instead of int ***values; Really. Now, I am not sure what context you would find int *** in. In 30 years of C code, I have yet run across this. But, just how do you retrofit STL into C code that old?
And now on to the "incomprehensible"
s->ss->dc.d->v + i
Well, you can't have an ss without an s (see the first part of my comment), thus s->ss
Within that, we have a dc, which is (most likely) a union. To save memory. Probably a "context", but I'd actually need a few more lines...
Choose d, which is a pointer, v (likely "value") because we are adding a local integer.
Really, the author IS just inexperienced. Problem is, he has a blog and seems to believe in preaching. He takes a joke at face value -- and doesn't think of the real reason...
I condemn him to coding on an 80x24 terminal, in C 89 (or K&R C), or FORTRAN IV for two years... After which he should revisit this article.
Just another "Cubible(sic) Joe" 2 17 3061
There are many books on the subject.
We are talking about MAINTAINING code. Not writing new code.
Standard C did not come with a linked list, stack or queue library. Suck on that fact. If your job is to maintain a such a program, you really have to come to grips with this.
Just another "Cubible(sic) Joe" 2 17 3061
I look at the language standard. Look at the if() statement. It is followed by one line, and a semicolon. No parenthesis. Parenthesis allow you to group multiple lines (blockify) - and its' always possible to replace any block of code with a one-liner (either a function call, a macro, or even using the comma operator).
Indent your code properly and you'll never have a problem with parenthesis - and your code will be more concise, thus easier to read.
Adding brackets increases the visual pollution,
WRONG.
It's a familiar pattern that the brain can process much more quickly and correctly. And thus much easier to comprehend than your "on-again, off-again" use of braces that introduces two patterns when there only needs to be one.
So, from a conceptual perspective, it's YOUR style that's more cluttered.
and I really hate it when people put the opening brace on its own line - its a waste of a line.
I'm sure that terabytes of storage would be wasted if you were to write all your code that way.
NOT.
Nice to see that you code to make your "folding editor" happy instead of the developers who have to maintain your code.
Folding editors can handle braces at the end of the line just fine - why can't people? Concise can be clear as well.
Well, let's see...
Good fucking God. Because humans aren't "folding editors"!!!!!
Because the human brain doesn't work the same way a computer does? Because a human brain sees and understands familar patterns faster and more correctly? Thereby making your code "sometimes there, sometimes not there" use of braces confusing at a fundamental level, and conceptually more cluttered than always using braces?
Do you REALLY expect humans and "folding editors" to work the same?
And YOU are trying to tell everyone how to write code?
Wow.
Just wow, someone who goes off on a rant about coding standards, and can't even defend his preferences without resorting to subjective characterizations like "cluttered"? And wonders why people can't understand code the way and editor can?
Even nicer to see you post that under your real name. Now I know where to file your resume, if I ever see it.
"Also, you NEVER should use tabs instead of spaces for indentation."
WRONG!!! You always should use tabs for indentation... PROPERLY!
"What happens if someone happens to use an editor that expands tabs to something different so that code no longer lines up?"
This CAN'T happen as long as you are using tabs properly: only on the rightmost side up to the right start of the block. I challenge you to find a situation where properly tabbing leads to misaligning due to changing tab width.
You're not correct. The C standard guarantees that:
(((ss != NULL) && (ss)) || ((ss == NULL) && (!ss)))
By virtue of the fact that:
1. A NULL pointer must compare equal to 0
2. "Boolean" statements are in fact integer statements where nonzero is true and 0 is false
3. Non-null pointers are not be equal to null pointers
As for C++, it has true booleans but ints still coerce to bool in the same manner, and therefore by extension pointer types still do.
If that doesn't work, your compiler is VERY broken. Even if the internal representation of NULL is not bit-pattern 0x00000000 (or however long it is on the target architecture).
When I'm coding in C, I adopt the best practices from http://lxr.linux.no/
I'd like to buy homeland for our 10 million people. http://twitter.com/mahadiga
And this is relevant to 99.9% of the software out there how?
Well, it's pretty much relevant to 100% of kernels in current use - and probably 100% of internal workings on consumer devices. Narrow range of work? hardly. .NET apps.
Get back to your blinkered Windows world and your
And replacing the cute one-liner with clearer (and just as fast, since it compiles down to the same instructions) code:
p = s->next;
while(p != NULL)
{
p = p->next;
}
ps: forget about 'NULL != p'. Only ancient compilers cannot detect inadvertent assignment inside evaluation statements [and a coder using such a construct purposely is certainly beyond help :)].
I don't comment much. I figure that if you understand the problem domain, my grammar will suffice... and if you do not understand the problem domain you have no business futzing around with my code in the first place.
I will curse and mock your name after you've left a position because you've context switched ten times to do work that should have been done in one well designed method, and now I've got a server that is puking because you don't know programming nearly as well as you know how to acquire whore points on slashdot.
As my grandfather, a noble and stoic man of few words would say, shut the fuck up.
Warning: This signature may offend some viewers.
You realize we're talking about C here, right?
"Also, you NEVER should use tabs instead of spaces for indentation."
WRONG!!! You always should use tabs for indentation... PROPERLY!
"What happens if someone happens to use an editor that expands tabs to something different so that code no longer lines up?"
This CAN'T happen as long as you are using tabs properly: only on the rightmost side up to the right start of the block. I challenge you to find a situation where properly tabbing leads to misaligning due to changing tab width.
You're ASSUMING the next knucklehead who was to maintain the code knows how to set tab stops properly.
Since you're entire argument is based on a questionable assumption, it fails.
1. The stl is totally inappropriate for ANY c program - it requires c++.
2. Sometimes you don't want the overhead of one of those generic one-size-fits-all solutions.
3. Are those libraries perfect? No? Well, gee, so when there's a bug, that's one more place it might have crawled in ...
4. Sometimes you want to take advantage of the extensions in c99.
Better yet.
for(p = s->next; p != 0; p = p->next)
Sure it's more verbose, but it's also more readable especially to a novice, since it's closer to the usual prototype of a for loop. (i =0; i xxx; i++)
Wrong. You can't assume NULL=0 and have portable code, although it will most probably work.
And proper indentation would have saved the day - no braces required.
It's one thing python actually gets right.
And your example just shows that a lot of people can't read code. If you're not dependent on braces to "clue you in", you'll spot the error immediately.
That's a problem with YOUR editor, not mine. there's a TAB key on your keyboard for a reason.
One of the reasons people try to use 4 spaces instead of an 8-space tab is because they let their code get fugly - WAY too many levels of indent and way too long variable names - and the only way they can see it on-screen without resorting to havig their code wrap every second line is to cheat - to only indent 4 spaces instead of a hard tab.
They're too unimaginative to come up with concise descriptive variable, function, class, and method names, and to stupid to realize that when your code has too many levels of nesting, it's "broken by design" and a great place for bugs to breed..
Arrogant little piss-ant, aren't we? I'm quite comfortable using vim on a remote box via ssh. I was writing code before the terms tui and gui were even in common use - we only had text screens. We had to code by pushing the bytes uphill, both ways, in a storm (well, not quite that bad, but monochrome amber monitors and hercules video cards give you an idea?). And in your example of "no bandwidth", a hard tab is better, since it's only 1 character. So who's the nobody? According to your definition, you failed since you sure wasted bandwidth. Like your examples - but at least you're consistent in that respect.
I guess you need to learn to be more fluent in the language. Next, you'll say that the comma operator should be banned because YOU might make a mistake in interpreting how it works. Or the hook operator. Or *gasp* those evil macros. And you'll also insist that we use that piece of shit called the stl when we're trying to make a multi-threaded app, and then wonder why performance is absolute crap.
Really? Next you'll say you can't read the first line of a paragraph, then skip to the next paragraph if you realize that what you're not looking at the right code. Do you move your lips when you read?
Look, if YOU have a problem with concise code, that's your problem. Your style went out back in the early '90s. It's still taught that way because teachers are inevitably a decade behind the curve - at the minimum. Only old farts who can't adapt, the people who learned from them, people who are trying to increase their LOC count, and old koreans still put the opening brace on its' own line, or are so "un-fluent" in the language that they need to blockify everything, and can't handle something as simple as an if() without braces. What next - putting braces around the contents of case statements "just in case"? I've seen people do crap that, and it tells me one thing - they don't really know c. For them, pascal is probably a better option, since you HAVE to put begin and end ...
Visual conciseness isn't a question of saving disk space - it's a question of making the code easier to read by reducing clutter. It works - problem is, you're so used to the crutch (yes, all those superfluous braces that the standard says aren't required ARE a crutch) of the clutter that you can't do without it. You're a crip. Admitting it is the first step in overcoming your handicap.
Make should be redundant, i.e. it should be possible to write software without having to hand edit a complex script that does nothing other than tell the computer how to put all the parts together.
const int one = 65536; (Silvermoon, Texture.cs)
SJW, n: "Someone I don't like, and by the way I'm a fuckwit" - AC
> Wrong. You can't assume NULL=0 and have portable code, although it will most probably work.
You can if it's ANSI C. The standard dictates:
6.3.2.3 Pointers
3) "An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function."
If p is a pointer type, "p != 0" should always work equivalent to a null pointer.
I look at the language standard. Look at the if() statement. It is followed by one line, and a semicolon. No parenthesis. Parenthesis allow you to group multiple lines (blockify) - and its' always possible to replace any block of code with a one-liner (either a function call, a macro, or even using the comma operator).
Wrong. Such code is just waiting for the following to happen:
Guess what that if statement will look like to the compiler? Brackets are very much better to use and make code clearer to both the programmer and the compiler for what is going on.
Note: While this situation specifically points out the issue with Macros, which are the most susceptible to it - it is by no means the only reason. Just one point why you got it wrong. And I am in no way saying that Macros are not good to use - sometimes they are, most of the time they are not.
Truth is like the sun. You can shut it out for a time, but it ain't goin' away. - Elvis Presley (source: imdb.com)
Just ask yourself: which is more valuable, the programmers' time, or the computer's time?
When your processor's running at 2GHz, optimizing a loop to squeeze 4 million instructions into 3.4 million instructions saves the computer less than 1/10 of a second, while costing the programmer how many minutes? At that's a generous guess, assuming that your compiler can't do a better job of optimization that the programmer, anyway.
In most cases, every programmer trying to learn your code (and that includes you, 4 weeks after writing it) is better served by making it as dead-simple-easy to understand as possible than trying to shave instructions.
Of course, then there are the special cases; if that loop is going to be run 500 times a second, then maybe the computer's time is starting to get more valuable, and shifts the calculation in its favor. Then the real question isn't when to be clever, but knowing how to measure that.
This is mostly b/c Microsoft (in their infinite wisdom) only enables Warning Level 1 by default, which catches almost nothing. Now it's not hard to turn on Warning Level 4, but then you also have to go through and fix all the stuff and learning which warnings to ignore from Microsoft's code - headers, etc. - that gets brought in. It's bad enough using MS APIs can cause memory leaks (my favorite being one that caused a 4 byte memory leak whenever used - and no, it there wasn't anything I could do to clean up anything to resolve it at my program's level; only a memory stack analysis was able to find what it was; may be they fixed this for Win7/2k8 with the refactoring they did...hopefully.)
Thankfully I'm mostly doing Qt development now...:->
Truth is like the sun. You can shut it out for a time, but it ain't goin' away. - Elvis Presley (source: imdb.com)
Please see the GNU Java Compiler. To quote from the site:
GCJ is a portable, optimizing, ahead-of-time compiler for the Java Programming Language. It can compile Java source code to Java bytecode (class files) or directly to native machine code, and Java bytecode to native machine code.
99% of the time yes, Java is JIT compiled to native code. But there are ways to binary compile it. (BTW, I too hate Java.)
Truth is like the sun. You can shut it out for a time, but it ain't goin' away. - Elvis Presley (source: imdb.com)
"You're ASSUMING the next knucklehead who was to maintain the code knows how to set tab stops properly."
So, in order for others no to impose you their "standard" tabstop your proposition is to impose everybody *your* "standard" tabstop?
And about knuckleheads, if you accept them disorganizing your source code, what will be next? Allow those knucleheads to write your source code? *That* is the real failure.
In my opinion this matter comes down to a balance of essence and ceremony in code. Essence is that much actual provides a function (business logic), and ceremony is simply that which we constantly repeat (program logic; i.e. error catching blocks, etc). One elegant one liner may provide essence in the code but at the loss of the ceremonial code that makes that code usable and easy to follow/maintain Personally I find the a best practice to maintain separation of those two elements such that ceremony can be mapped in through a defined path, without being overly verbose. In other words, keep your business logic separate from program logic.
In good practice this will help coders avoid the need for clever one liners so long as the business logic doesn’t interfere with the program logic. In some cases when a clever one liner may be required they are only in the business logic and shouldn’t interfere with the program thus making it much easier to folllow without having to trace it all over the place.
An example of this may be using a lambda invocation pattern to wrap error catching around a database factory implementation such that all calls to the database happen through one interface/class implementation. This allows you to change the business logic without touching ANY of the program code. I hope that makes sense, and I hope Im not misunderstanding the question either! lol
I think the word you are looking for is compiled, and there have been AOT compilers for Java a long time. Also, the compilation overhead for any non-trivial application is generally negligible. And who says you can't cache compilations? What, OS not secure enough? Not my problem.
I know tobacco is bad for you, so I smoke weed with crack.
You know what is the most horribly documented code in the history of the world? DNA
Clever solutions is a tool in the hands of a programmer who uses a compiler that cannot do the clever job itself. This is currently, most of all compilers/translators out there and more importantly, most of the "widely-used" (an oxymoron!) ones. In other words, if your compiler is stupid, it is your job to produce the optimization by altering readable code into code that performs efficiently but looks too funny. Maybe languages like Haskell or ML will liberate us from this. It is a culture, a culture that lacks education.
Always rely on the compiler. It is your translator, and as with any good translator, you can relax speaking your native language (i.e. human) and trust it to do more an adequate job.
I agree!
I have excellent Karma and I am not afraid to Troll it.
I use if() {...} brackets because my readers expect it. That's enough reason. We have all kinds of crappy bracket positioning and indentation styles, but the brackets are always there (except in some special pattern cases). As for your example:
But the greater point is, that when maintaining code something small can have a big effect on the program as a whole. Get someone who doesn't know much about macros, and the problem explodes - especially when you have a program where (for whatever reason) you can't replace the macro with an inline function (and there are cases, though rare).
Also, while I've read a lot of macros - I've never seen anyone wrap it in a loop like you suggest. More likely than not macros are written as follows:
Truth is like the sun. You can shut it out for a time, but it ain't goin' away. - Elvis Presley (source: imdb.com)
http://www.jlab.org/~saw/linux/lug_19091999.mgp
What is this?
You also need to take into account the space the .c file is going to take before it's compiled too. I just took a look at a smallish program I made, consisting of about 3800 lines of source. It's 108k, which is not a trivial amount of ram on a computer from the early-mid 1980's.
often people who complain about hard to follow code are simply poor performers. I've had people complain about this and then go ask 4 others what the code did and they all answered after looking at the code for 60 seconds. So obviously it was not the code....
What happens when you get one of these types and you actually listen to them. You end up with code that spans 4 pages instead of 1 page. Then take that same code to other people and time them on how long it takes them to understand it. Time will double.
So really if people start complaining about tricky code, then you have to take a long hard look at them. I personally can't think of ANY code I found to be tricky unless it was in a contest for that purpose. I have spent much more time examining code that should have taken seconds to look at rather than the minutes it did because it was part of a overall design philosophy involving a layered approach etc.
writing c code
designing in c or c++
leave c programming to c programmers
"C", "C++".