Kuro5hin.org: technology and culture, from the trenches
create account | help/FAQ | contact | links | search | IRC | site news
[ Everything | Diaries | Technology | Science | Culture | Politics | Media | News | Internet | Op-Ed | Fiction | Meta | MLP ]
We need your support: buy an ad | premium membership

[P]
Resistance to "good" programming techniques

By terran in Op-Ed
Sat Oct 28, 2000 at 03:27:25 AM EST
Tags: Software (all tags)
Software

There are two general ways in which code can be beautiful - conceptual simplicity and efficiency. The first of these goes hand in hand with "good" coding practice, but the second does not. My hypothesis for today is that this is why some programmers resist pressure to write code in a way that makes it easy to understand.


I'd like to begin with two examples of things I think are beautiful and elegant. The first of these is Smalltalk. Smalltalk isn't in common use these days, and suffers many of the same vendor difficulties as Lisp, but conceptually it is one of the most beautiful things I have ever seen. If you've never looked at Smalltalk, I'd definitely recommend it. To summarize, everything in Smalltalk is done by passing messages to objects. Even arithmetic. Conditionals are handled by passing a message such as ifTrue to a boolean object, with the code to be evaluated in the true case as its argument.

Smalltalk is beautiful because of its conceptual simplicity and coherence. The other way in which something can be beautiful is efficiency, efficacy, or some other sort of functionality. My example of something beautiful in this way is Duff's Device. Duff's Device makes the most of allowable C syntax; it is less than easy to read (until you "get it"), but it does its job rather well.

Programming is an art, and these are two of the ways in which one can be artistic. By this I mean that they are done for their own sake, because of the satisfaction of achievement. This is not to say that there aren't material rewards - simple concepts can lead to easier understanding, and efficiency leads to programs that run faster. However, the artist seeks these goals even when there is not a compelling need for them.

It is this characteristic which is the problem, particularly when it is applied to efficiency. This is because writing code in a fashion which makes it easier to understand often requires making it slower and less efficient. The efficiency loss may not be much - doing a few redundant tests on a variable to simplify control flow and avoid a goto, for example. The practical implications are frequently negligible. That doesn't matter, however. The whole point of art is that it is inherently desirable, and damn the practical implications! When you tell someone to write code that's less efficient for the sake of clarity, you're telling him to give up his art for practical considerations. That's never going to go over well!

Imagine saying to Michaelangelo: "We want you to use fewer colors on the chapel ceiling. We know it will make it look slightly worse, but the effects are negligible, and nobody will notice from that distance anyway. However, it will make it much easier for the people who maintain your work if you just use the standard colors that are easy to mix." I can't imagine that would have gone over well. Telling a programmer to sacrifice the functionality of his code to make it easier to understand by someone who will maintain it later is the same.

The advocates of clear programming suffer as a result of this. There is resistance not only in the specific areas where clarity is actually at odds with efficiency, but everywhere, because the "clear and maintainable" school of thought is often perceived (consciously or otherwise) as being in conflict with art, and thus classified as the enemy.

The first type of elegance I mentioned, conceptual simplicity, rarely conflicts with a desire for clarity, but it can conflict with other practical considerations. This happens when there is a need to do things in a hurry for economic reasons, but a programmer wants to take longer to do it the "right" way.

It seems there are several approaches which could be taken:

  • Force people to do it anyway. Sacrificing art to practical considerations is often necessary, and there's no inherent reason that programming should be any different. However, it pisses off the artist. If the competition is less strict about this, people may desert for the better "quality of life" provided elsewhere.
  • Hire people who don't see it as art. This certainly solves the problem, but the "artists" are also often the most talented (including, perhaps, a generation of Real Programmers)
  • Sacrifice the future for the present. If you give people more leeway to be artistic, you'll get code that takes longer to write now and/or longer to understand later. However, it will keep the people writing it happier now, and happy people do better work. The tradeoff may be worthwhile.

None of these solutions are perfect. However, I think it would be a step forward to even acknowldge the reasons for the problem, and move coding style one step further from a religious war to a debate where both sides can be seen as having valid motivations. Even when it's necessary to force people to do it the practical way, one will almost certainly get better results by acknowleding that one is asking for a sacrifice than by taking the "if you choose a minimal efficiency increase over clarity, you must be an idiot" attitude that seems common today.

Sponsors

Voxel dot net
o Managed Hosting
o VoxCAST Content Delivery
o Raw Infrastructure

Login

Poll
Is programming art?
o Of course - if it weren't, I wouldn't love it. 28%
o Yes, but clear and maintainable code is beautiful too. 45%
o Yes, but only as a luxury. When you're being paid, it's unreasonable to expect to enjoy it. 7%
o A few pieces are, but mostly it's not. 11%
o Not at all. Lay off the crackpipe, art boy. 6%

Votes: 138
Results | Other Polls

Related Links
o Smalltalk
o Duff's Device
o Real Programmers
o Also by terran


Display: Sort:
Resistance to "good" programming techniques | 62 comments (62 topical, editorial, 0 hidden)
Approach 4... (3.37 / 8) (#1)
by magney on Fri Oct 27, 2000 at 11:09:55 PM EST

...hire people who see clarity and reusability, rather than raw efficiency, as art. :-)

Efficiency is best sought when you've completed the design and identified the bottlenecks, as Duff himself clearly knew when he invented his Device.

Do I look like I speak for my employer?

Art is for the viewer, not the artist? (4.00 / 2) (#14)
by cadrys on Sat Oct 28, 2000 at 10:05:11 AM EST

Efficiency is best sought when you've completed the design and identified the bottenecks, as Duff himself clearly knew when he invented his Device.

Indeed. The original architect(s) need to view clarity and well-defined functionality as art. The first iteration of "finished product" shouldn't care about efficiency. NOW send in the tweaker(s) to identify bottlenecks and make a few changes, if needed. If something breaks, don't tweak further, but rollback and decide how badly that one loop needs to be sped up.

This does not apply, of course, in real-time programming (i.e. telephony) where every millisecond counts. Most of the code that gets written does not fall in this category.

"Art" IMAO is less a function of "beautiful" source code and more a function of a well-running program that does everything the customer wants, and nothing they don't want.


--- A Knight in White Satin -- have compiler, will travel
[ Parent ]

Dangerous (5.00 / 1) (#30)
by Simon Kinahan on Sun Oct 29, 2000 at 12:01:30 PM EST

This is a dangerous approach. Architects, or at least people capable of architecture level thinking (see my other post on this story), really must be involved in the process of performance optimisation. If you let the tinkerers make arbitrary changes to a system at the optimisation stage (usually just after the product is meant to have shipped) they'll likely destroy the interfaces between the architectural components. I've seen this happen twice in my 5 years in the industry, and have read other things that suggest it is common.

The best approach to optimisation is global, and usually does involve redesign and architectural change. Small local optimisations, especially the kind that mess up architectural interfaces, are generally not very effective.

Simon

If you disagree, post, don't moderate
[ Parent ]
Two kinds of art (4.50 / 12) (#2)
by fluffy grue on Fri Oct 27, 2000 at 11:22:40 PM EST

One thing which leaves me somewhat unsatisfied with your essay is that (from my initial reading of it, anyway) it seems that you're trying to make the case that "because it's art, it shouldn't have to be done right."

One of the defining characteristics of art is that it lasts. To use your own analogy, Michaelangelo's painting on the Sistine Chapel is art which is very well-composed, and was done properly so that it has stood the test of time (well, at least it's lasted several hundred years). It's amazing how much code degrades very poorly with time simply because it uses nonobvious stuff which, frankly, tends to break down over time. How much old code "broke" between libc5 and libc6? The code itself was always broken in the respect that it made bad assumptions about the semantics of libc calls (much of my code had to be "ported" for example, and every single problem was my fault). Also, there's a lot of old demo code of mine which just plain doesn't work anymore since it was hardcoded to some very specific characteristics of an OS and a machine. It was art at the time, but it is now spoiled. It didn't last. All because of my crappy coding technique.

As far as efficiency vs. readability: I've found that over time, my code's only become more readable and more efficient, while being quicker to produce. Proper use of the STL, for example, has given my code much more efficiency with much less effort than ever (don't laugh - thanks to the STL, a lot of my code runs in O(lg n) time when it would have normally run in O(n) or even O(n^2) if I'd done a naive hand-rolled implementation), and it's much more readable, and so forth. Not to mention more powerful, flexible, and, well, elegant. Compare the two following code snippets:

Mnode *MFindChild (Mnode *n, char *name)
{
        int i;

        for (i = 0; i < n->nc; i++)
                if (!strcmp(name, n->c[i]->name))
                        return n->c[i];

        return NULL;
}

void AddChild (Mnode *n, Mnode *c, char *name)
{
	n->c = realloc(n->c, n->nc*sizeof(Mnode *));
	n->c[n->nc] = c;
	n->nc++;
}
versus:
SceneNode *&SceneNode::operator[](string name) {
	return m_children[name];
}
The first snippet of code is from my first 3D engine. The second is the equivalent functionality (finding an object's child by name or adding a child) using the STL (m_children is of type map<string, SceneNode *>). Not only was the second bit easier to write and much more maintainable (I didn't even want to look at the old code for ADDING a child), but it's much more general-purpose (the reference returned by GetChild means that I can use that function for manipulating the scenegraph as well - and in fact, that code does double for adding a child), and it executes in O(lg n) time, rather than O(n). Also, although I reformatted the second code to stand alone, it's actually an inline declaration in the SceneNode object's header file, meaning that the code gets nicely inlined for even more speed (given sufficient compiler options).

In any case, back to the original thingy: do you really want to hire an artist who doesn't care enough about their art to even do it right? I mean, this isn't stuff like style or drawing lines, but mixing the paint to begin with. Like, you wouldn't want an outdoor mural painted in watercolor.
--
"Is not a quine" is not a quine.
I have a master's degree in science!

[ Hug Your Trikuare ]

What's "right"? (4.37 / 8) (#6)
by terran on Fri Oct 27, 2000 at 11:53:35 PM EST

I think part of the problem is a lack of agreement as to the meaning of "right". In your example, I rather agree with you that the second approach is better. Let me see if I can find an example...
for (i=0; i<num_whatever; i++)
{
	if (datastructure[i]->somemember == desiredvalue)
		break;
}
if (i==num_whatever)
	return -1;
else
	return i;
Steve McConnell (Code Complete, p. 335) says that the code above is bad. His reasoning is basically that it is too confusing, either for the programmer, or for people reading the code. He says one should write:
int found=0;
for (i=0; i<num_whatever; i++)
{
	if (datastructure[i]->somemember == desiredvalue)
	{
		found=1;
		break;
	}
}
if (!found)
	return -1;
else
	return i;
I don't agree. I find the first version of this more aesthetically pleasing. The memory required by a single integer is utterly negligible, and I fully acknowledge this. In principle, however, I still find it unnecessary and wasteful, and it rubs me the wrong way. It's aesthetic, not practical.

Furthermore, I'm fully in favor of documenting code, with comments. The less obvious it is, the more comments one puts, explaining what was intended. What I'm against is making the execution itself less efficient for the sake of making it easier to follow.

[ Parent ]

Java vs C (2.85 / 7) (#8)
by Dacta on Sat Oct 28, 2000 at 12:49:59 AM EST

If you were using a reasonably modern language instead of something that should have died with the eighties, it would the compiler would complain about the first example of you wrote it properly:

for (int i=0; i<num_whatever; i++)
{
	if (datastructure[i]->somemember == desiredvalue)
		break;
}
if (i==num_whatever)
	return -1;
else
	return i;

(Complains because of scope problems)

It is better the second way because it keeps the looping functionality separate from the "found" functionality. In the second version, you can read the code:

if (!found)
	return -1;
else
	return i;

On its own, and realise that it returns -1 if something is not found, or 1 if it does. You don't have to look at the loop and understand what's going on there, at all.



[ Parent ]
Scope... (2.66 / 3) (#11)
by pb on Sat Oct 28, 2000 at 03:18:35 AM EST

That's a more recent C++ thing; in C, you'd have to explicitly declare it at the top of a block anyhow.

For the record, I prefer this:

blah(Dat * d, int i, int v) {
while (i)
    if (d[i--]->foo == val)
       return i;
return -1;
}

...but maybe I've read K&R for too long. :)
---
"See what the drooling, ravening, flesh-eating hordes^W^W^W^WKuro5hin.org readers have to say."
-- pwhysall
[ Parent ]

Re:Scope (3.00 / 1) (#15)
by Dacta on Sat Oct 28, 2000 at 10:10:08 AM EST

I thought it was C++, too, but I tried a for loop in VC++ (because I had it open at the time), and it didn't complain when I accessed the counter outside the loop. I was fairly sure it should have given a warning at least. That's why I said Java - I know it works like that.

BTW, your code is really nice, but it took me a few moments to figure it out. Maybe it's because I haven't done a lot of recent C/C++ coding.

I wonder how much familiarity with a language influences maintainability?



[ Parent ]
Thanks, but... (4.00 / 1) (#26)
by pb on Sun Oct 29, 2000 at 04:22:13 AM EST

I admit, I deliberately introduced a bug; Carnage4Life caught it, but obfuscated my code further. :)

I replied with an even-yet-still-more obfuscated version, which should be smaller and faster, IF I took the one piece of purely unnecessary peice of obfuscation out (obfuscating a constant...).

It took you a few moments, because I rely heavily on C's defaults. C assumes that a function with no return value returns an int; it assumes that a literal 0 means false in a condition, and anything that isn't zero is true. Also, it has the infinitely abusable and destructive ++(exp), (exp)++, --(exp) and (exp)-- operators, that Scheme programmers probably should hate.

Incidentally, the equivalent Scheme implementation of this loop would be really slow in C, unless tail-recursion was implemented properly in the C compiler. In my experience, it isn't. (there is some code in gcc for this, but it often doesn't do anything; don't ask me why...)

Familiarity with a language *should* influence efficiency. Anyone else trying to mess with the code will probably (a) mess it up, and (b) reimplement it worse if they don't know what the author was doing. That's why we comment code, and mention when a crufty function shouldn't be changed. ;)

I've seen people write code like this to get a power of two...

int getpow2(int n) {
return (int)pow(2,n);
}

This might be very straightforward and maintainable, but it's instantly abhorrent to anyone who knows the language (and how to use it) well; what they should be doing is this, and they really shouldn't need a function for it.

getpow2(int n) {
return 1<<n;
}

Or, more simply:
#define getpow2(n) 1<<n

This assumes that n is indeed an int, or evaluates to one; if you're *really* paranoid, do this:
#define getpow2(n) (1<<(n))

An appropriate comment might be "gets a power of two", or "shifts a bit left n places to obtain two to the n"; if you don't understand that, you *really* shouldn't touch this code! ;)

An Unwitting Schemer might not use floating point, but still write this code, all the while complaining about crufty C syntax...

int getpow2(int n)
{ if (n>0)
{ return (2*getpow2(n-1)); }
else { return 1; }
}

I assure you that isn't cool in C, because it still takes O(n) time, and function calls are not fast. Hopefully the compiler optimizes the "2*blah" into a shift or an add, and falls through instead of doing the "else"; however, it probably won't optimize for tail-recursion, and it *certainly* won't recognize what you're doing, and just shift n places...

Therefore, when in Rome, learn the language first! Then watch them at work for a while before you destroy an aqueduct, and flood the city.
---
"See what the drooling, ravening, flesh-eating hordes^W^W^W^WKuro5hin.org readers have to say."
-- pwhysall
[ Parent ]
VC++ loop counters (none / 0) (#41)
by AndrewH on Mon Oct 30, 2000 at 08:44:02 AM EST

I thought it was C++, too, but I tried a for loop in VC++ (because I had it open at the time), and it didn't complain when I accessed the counter outside the loop.

Microsoft still implement an earlier version of C++ that extended the scope of loop counters to the next closing brace for access or reuse. If you prefer to limit the scope to the loop, you can try compiling with the argument /Dfor=if(true)for.


John Wilkes Booth, Lee Harvey Oswald, John Hinckley Jr — where are you now that we need you?
[ Parent ]
A very good example of bad code (3.66 / 3) (#17)
by Carnage4Life on Sat Oct 28, 2000 at 12:22:53 PM EST

It is interesting that this article talks about deliberately obtuse code that some people call art which is in fact bad programming.

Any code that takes someone more than a second to look at when all it performs is a simple iteration is badly written. Why do this, which takes a few seconds to grasp, and thus can easily lead to bugs when modified:

blah(Dat * d, int i, int val) {
while (i)
    if (d[i--]->foo == val)
       return i;
return -1;
}

when you could have just do this:

blah(Dat * d, int i, int val) {

my_type curr_foo;

for(int j=i; j-->0;  curr_foo = d[j]->foo ){
    if(curr_foo == val)
       return j;
}
return -1;
}

which is much more intuitive and clearly indicates that the action performed is a mere iteration without obsfucation. I am constantly irritated when I see code where people use clever ways to perform a simple loop and compare operation simply because they think it is cool to save a temporary variable or two, only for the code to be a source of confusion to fellow developers and a bug magnet later on.

I suggest reading Kernighan and Pike's The Practice of Programming which gives better examples and rationale for not using obsfucated or clever code to perform simple tasks.



[ Parent ]
your example is not any clearer (4.00 / 2) (#23)
by klash on Sun Oct 29, 2000 at 01:20:39 AM EST

I honestly find the original example to be much clearer. A few reasons:

  • you introduce another single-letter, randomly-named variable that I have to keep track of. "j" and "i" look very similar, which adds to the confusion.
  • You perform the variable update in the conditional part of the "for"
  • You perform variable assignment in the update part of the "for". (I think this would definitely qualify as "clever")

I understood the first example instantly, but then again that's the kind of code I'm used to writing...



[ Parent ]
Heh heh. (3.00 / 1) (#25)
by pb on Sun Oct 29, 2000 at 03:59:35 AM EST

I was wondering if anyone would spot the bug...

I consider the temporary variable in your code useless, ugly, and wasteful, but yes, I did do my decrementing in the wrong place, destructively modifying i. :)

A simple re-write would only involve moving the i-- to the end, or if you're converting it to a for loop, to the proper place. Your code is *way* too verbose, and "my_type" should be an int.

I don't think it gets much clearer than this, and it's efficient, too, but the semantics for "post-decrement" *are* somewhat whacked; the PDP memory architecture is probably to blame for this, it's all in the Lyons book.

Also, one reason I rewrote it is because this sort of code should compile better, at least to an x86-style architecture, and probably anything where zero is false, and ends a loop.

My code is terse, but it isn't that obfuscated; it's just written in C. Like I said, read K&R; they'd do much the same. I admit that this is somewhat obfuscated, although it should do the same thing, correctly and somewhat efficiently; it might be instructive to see *why* this works. :)

blah(Dat *d,int i,int v) {for(;(i--|(char)(*""))&&(v^(*d[i]).foo););return i;}

Anyhow, it shouldn't be a problem, provided that you indent your code properly, stick to some common idioms, and don't use temporary variables when they aren't needed. Rearranging code somewhat yourself to ensure that the compiler does The Right Thing isn't as useful nowadays, but knowing what it should be doing (and checking to make sure) is good. Also, writing your code so that it always does the same thing regardless of what the compiler does isn't necessarily a bad thing, especially if this function is the bottleneck. (note: something this dumb should probably be inlined, too; hopefully your compiler will do that for you as well as eliminating your extraneous variables, as well as picking the fastest way to do common operations, and evaluating constants at compile time as much as possible...)
---
"See what the drooling, ravening, flesh-eating hordes^W^W^W^WKuro5hin.org readers have to say."
-- pwhysall
[ Parent ]
RE: Heh heh (2.00 / 1) (#51)
by Carnage4Life on Tue Oct 31, 2000 at 01:15:03 AM EST

I consider the temporary variable in your code useless, ugly, and wasteful, but yes, I did do my decrementing in the wrong place, destructively modifying i. :)

After working on a number of team projects as well as working as a TA for two programming classes, I have come to understand the value of chosing clarity over program efficiency because it is far more important to optimize development time than execution time especially in this day of 1GHz machines with 1GB RAM.

The rest of your comments seem to indicate a penchant for outthinking the compiler which I consider quaint in this day and age. A smart compiler with optimizations turned on will eliminate most of the bloat from my code and my code would be clearer to the average developer than yours would be. I feel it is the sign of an unfortunate trend that experience has taught me to dumb down my code but that is the reality of the software industry.

Finally, on the advice of more experienced programmers and books, I do not add any platform-specific/compiler-specific optimizations to my code until I have profiled it and found out where the bottlenecks are because they are rarely located where you suspect.



[ Parent ]
Pretty much... (none / 0) (#58)
by pb on Wed Nov 01, 2000 at 10:53:52 AM EST

Well, some of this comes down to opinion and style; I don't consider your code to be more readable either, and longer code isn't necessarily better, while more verbose code might be... :)

Sometimes you're right about optimizing compilers; they do a pretty good job, but you can't always trust them for that. Even with fast computers, a speed-up of a factor of 2 (or 10, for that matter...) is still *very* significant. Just try compressing a large file... Of course most of the speed-up will come from having a good design in the first place, but some compilers do really stupid things. Never trust a C compiler to do the right thing with a recursive function...

I do agree that profiling is a good way to speed up your code, though; I wrote a simple filesystem and used a simple modular design; then I noticed that I was reading a sector (from RAM) for every single *bit* I read. Well, needless to say, the routine for finding the first 0 bit in a bitfield was *very* inefficient.

I sped that up a *lot* by adding a routine that checked up to 32 bits at a time, and could have sped it up much more by writing a few custom routines to simply read the whole sector and do the seeking once. (but wanted to keep my code more generic than that; I had a couple of different bitfields using the same code)

And yes, I found this all out by profiling my code. That's a great tool; it's just that I also optimize where possible anyhow; after you've done it a few times, it doesn't take any extra thought. Think Globally, Act Locally, you know?

Oh, and did you like my obfuscation? I admit that the '(char)(*"")' was a bit much, but I had fun. My friend Joe's translation of it was much cleaner than anyone's code so far IMO, but he's just an excellent coder.
---
"See what the drooling, ravening, flesh-eating hordes^W^W^W^WKuro5hin.org readers have to say."
-- pwhysall
[ Parent ]

for (initialiser;test;increment) body; (4.33 / 3) (#40)
by inpHilltr8r on Mon Oct 30, 2000 at 03:04:18 AM EST

The increment part of the for construct is evaluated after the body, and before the test.

thus:

1: curr_foo is uninitialised the first time round the loop
2: j will return 1 less than the desired index

I suggest re-reading K&R.

[ Parent ]
Corrections duely noted (none / 0) (#46)
by Carnage4Life on Mon Oct 30, 2000 at 02:43:42 PM EST

You are correct, on reexamining my code, I realize that doing the assignment in the update part of the for loop is as fraught with problems as the code I was correcting.



[ Parent ]
actually (3.00 / 1) (#32)
by mikpos on Sun Oct 29, 2000 at 01:26:30 PM EST

Both C++ and C (as of C99) support declarations mixed with statements (so you don't have to put them at the top of a scope). If your C compiler doesn't support it (and none do, to my knowledge), then you can just as easily introduce a new scope.

[ Parent ]
both of these look bad to me (4.20 / 5) (#9)
by madams on Sat Oct 28, 2000 at 01:50:38 AM EST

I don't think either of the code examples are very good. I would write such code as:
for( int i = 0; i < num_whatever; i++) {
if( datastructure[i]->somemember == desiredvalue) {
  return i;
 }
}
return -1;

Even better would be:

int index = -1;
for( int i = 0; i < num_whatever; i++) {
if( datastructure[i]->somemember == desiredvalue) {
  index = i;
 }
}
return index;

My first example is bad because it hides return statements inside the body of the code, rather than just at the end. The second example fixes this problem.

I had always heard that &amp;quot;Code Complete&amp;quot; was a great book. I'm a little offended that the book considers your second example as &amp;quot;good programming&amp;quot;.

I think it's bad programming style to require extraneous state variables that you have to keep track of when reading the code (such as the found variable).

--
Mark Adams
"But pay no attention to anonymous charges, for they are a bad precedent and are not worthy of our age." - Trajan's reply to Pliny the Younger, 112 A.D.
[ Parent ]

Ouch (4.00 / 1) (#39)
by inpHilltr8r on Mon Oct 30, 2000 at 02:52:26 AM EST

Your second example doesn't break out of the loop the first time the test works, and thus isn't the same code, as it actually returns the last item to match, rather than the first (although if the keys are unique, that isn't a problem). It also takes longer, although, it's probably a couple of op-codes shorter.


[ Parent ]
Um... (3.00 / 1) (#57)
by dlc on Wed Nov 01, 2000 at 08:21:19 AM EST

What about:

return { map { $_>{'somemember'} => $_ }, @datastructure }->{'desiredvalue'} || -1;

Have we no Perl fans here?


(darren)
[ Parent ]

Bad Programming Practices are NOT Art (3.85 / 14) (#3)
by Carnage4Life on Fri Oct 27, 2000 at 11:22:49 PM EST

Nice article but I disagree with your glamorization of shoddy programming practices as art. In my short experience as a programmer I have only seen two types of problems where good, clear design should be put aside for performance enhancing, esoteric hacks and these areas are graphics and operating systems.

Almost every other place where performance enhancing hacks are used in software design, they cause more problems than they solve either by making the software difficult to maintain, creating security problems or BOTH.

I have noticed that most of the developers who value perceived efficiency over well designed, robust code are those that learned C as a first language or program primarily in C. I realized that this is mainly due to the fact that the standard C libraries are horribly designed and usually value efficiency over smart programming which then seeps into the coding practices of C programmers. Classic examples of myopically designed C libraries abound (e.g. str* and *scanf family of functions) which have gone on to cause more problems (buffer overflows, non-deterministic bugs, etc) than any gains from the efficiency of the methods.

PS: Gotta go, I had a really long comment planned but the S.O. just showed up. :)



*ahem* (3.80 / 5) (#4)
by fluffy grue on Fri Oct 27, 2000 at 11:25:43 PM EST

Graphics is not a place for ad-hoc hackery. Good graphics code isn't simply thrown out when you're done with it - it's a continual learning process, and it really sucks to not be able to go back later and relearn old tricks and the like.


--
"Is not a quine" is not a quine.
I have a master's degree in science!

[ Hug Your Trikuare ]
[ Parent ]

.. (2.50 / 2) (#16)
by ameoba on Sat Oct 28, 2000 at 11:14:49 AM EST

I'm sure you could make the same argument about coding anything. Given enough time, ugly code is not neccessary. It's only when you can't wait a few years for computing power to catch up with what you want to do that you need to use intricate, dirty hacks.

That said, there are always those that want to do more than a computer can. Look at the Demo Scene (in any of it's incarnations, tho' I'm only familiar w/ the PC and C=64 branches...) for an example of Art in Code. I'm not too familiar with anything current, but check <u>Second Reality</u> from the Hornet Archive, and keep in mind that that was done in 92-93 and would run without dropping frames on my 486.



[ Parent ]
Ha ha ha (3.00 / 1) (#24)
by fluffy grue on Sun Oct 29, 2000 at 01:37:09 AM EST

You don't know who you're talking to, do you. :)

Anyway. Hardware-level hacks can still be done elegantly. If nothign else, "dirty" hardware-level hacks need to be formatted and commented MORESO than non-dirty code so that you can actually understand it later.
--
"Is not a quine" is not a quine.
I have a master's degree in science!

[ Hug Your Trikuare ]
[ Parent ]

No exceptions ! (3.50 / 2) (#31)
by Simon Kinahan on Sun Oct 29, 2000 at 12:02:42 PM EST

Graphics and operating systems *do* require good abstraction. In graphics the abstractions tend to be very mathematical, whereas in operating systems they tend to be hardware orriented.

Simon

If you disagree, post, don't moderate
[ Parent ]
don't knock C (5.00 / 1) (#34)
by mikpos on Sun Oct 29, 2000 at 01:36:45 PM EST

There are a lot of bad C programmers. My guess is that there are a lot of C programmers period. I've never found any evidence that (in terms of ratios) C programmers write less robust code than non-C programmers. I suppose (for varying definitions of "robust") you could say that strongly-typed languages (like Haskell) are infinitely robust, no matter how bad the programmer is, but I'll leave those languages alone :)

C is not a bad language, though, and it does not encourage bad programming practices. Yes, gets(), *scanf(), str*(), etc. can introduce bugs (and in fact many of them must introduce bugs), but you very quickly learn which standard functions are good and which are bad. Surprisingly enough, you then learn to avoid the bad functions and bad coding practices.

[ Parent ]

Don't underestimate the importance of maintenance. (3.50 / 8) (#5)
by Greyjack on Fri Oct 27, 2000 at 11:34:36 PM EST

Telling a programmer to sacrifice the functionality of his code to make it easier to understand by someone who will maintain it later is...

...frequently *very* important for code that's going to be in use for any significant period of time. Indecipherable code usually gets replaced when it can't be maintained efficiently.

Obviously though, it's much better to find a more understandable design if possible. They're usually more efficient anyway, both in execution speed and maintenance (cf. fluffy grue's example in another reply).

--
Here is my philosophy: Everything changes (the word "everything" has just changed as the word "change" has: it now means "no change") --Ron Padgett


Whoa. (2.80 / 10) (#7)
by inspire on Sat Oct 28, 2000 at 12:18:21 AM EST

Hire people who don't see it as art. This certainly solves the problem, but the "artists" are also often the most talented (including, perhaps, a generation of Real Programmers)

IWPTA Perl programmers. Yeech.
--
What is the helix?

RE: Whoa. (4.25 / 4) (#27)
by chuq_r on Sun Oct 29, 2000 at 05:14:15 AM EST

I'm not sure what "IWPTA" means, but it sounds bad. As a recovering Perl programmer, I can indeed say that Perl code does have a tendency to be terse, obfuscated, and confusing -- especially when written by someone with a deep understanding of the language. Which isn't to say that Perl is useless or bad. I personally still think it's probably one of the most useful langauges created even if it isn't the most beautiful. As Leo Tolstoy said, "It is amazing how complete is the delusion that beauty is goodness."

The relatively complex syntax of a language like Perl is wonderul in the same way that the English language is wonderful: both give you the freedom create things that look very ugly or very pretty or fulfill some other desired qualification. But even a very rudimentary or somewhat incorrect understanding of both tends to still allow you to get your job done, even if it's in a very inefficient way. "Romeo, O' Romeo! Wherefore art thou Romeo?" could be seen as an artistially beautiful long-hand for "Yo! Why you gotta be a Capulet, bitch?" which generally gets the job done better in today's world, though it is not exactly aesthetically pleasing. :)

And of course you can write bad code in other languages as well. It's definitely not limited to Perl (though there is a bit more of a tendency there).

But the main point I'm trying to make is that beauty is not always a required necessity in code and as many good Perl coders and also many good Real Programmers will tell you, sometimes it actually stands in the way of getting the job done. (Of course, Perl programmers and Real Programmers are the most annoying of sorts and nobody can stand them. Right? Right? They should just drop of the face of the earth and we'd be better off without them!)

The difference I'm basically seeing is that one camp places a lot of value on the means to the end while the other places more on the end itself, means be damned. And this isn't even getting into the point of that whole "beauty is in the eye of the beholder" business...

Chuq

[ Parent ]

I agree entirely. (2.50 / 2) (#28)
by inspire on Sun Oct 29, 2000 at 06:07:47 AM EST

IWPTA = "I Wacky Parsed That As..."

Basically means that on first glance, I read the phrase "Real programmers" as "Perl programmers" and did a humourous double take.

Evidently not many people on k5 read alt.religion.kibology.
--
What is the helix?
[ Parent ]

Hmm... (2.80 / 5) (#10)
by Zarniwoop on Sat Oct 28, 2000 at 02:13:54 AM EST

I think one of the defining things about programming as art is that the most beautiful code is generally both efficent and maintainable. I've wrote my share of weird code- I wouldn't want to try to maintain anything from when I was learning C. But as I've learned more, I've decided that modularization and coding style is definately a good thing.

In regard to Michaelangelo, it seems more like saying "Don't make your painting a mess of color splashes and unrecognizable symbols" to me. Code may be a form of art, but it is a very purposeful form of art. We have to restrain ourselves with design, otherwise it all too often ends up with a beautifully tweaked, completely incoherent mess, that does one thing and one thing only- and whoever has to change it in the future be damned. A hack. (not that hacks don't have their place)

Creating code, IMO, is a process that takes time and patience. Efficency and maintainability are both extremely important. The thing that is produced is beautiful because of both of those things. Otherwise, you get some efficent, but highly confusing code.

You are mistaken (4.10 / 19) (#12)
by joto on Sat Oct 28, 2000 at 09:14:59 AM EST

There is no controversy between art and clarity...

The true artists always produce clean, maintainable code. Here is a useful classification of programmer-mastery.

  1. The artists
    The real art-form in programming is to produce clean, maintainable, provably correct, stable code as solutions to real-world problems. And the programmers who can do that deserves to be called artists. There are very few programmers at this level. Curiously, most of these people are also excellent mathematicians.
  2. The pragmatists
    Most good programmers produce code that is "good enough". They are pragmatists. They also see programming as an art form, but due to time-constraints they will never produce the fine art you see at level 1
  3. The tinkerers
    These are the ones you call artists. A tinkerer is someone who masters programming, but doesn't have enough real-world experience to value maintainability. Instead he (a tinkerer is almost always a he) amuses himself by producing obscure code that no one else understands, because this gives him a sense of superiority.
  4. The beginners
    A beginner is someone who doesn't completely know how to do it. They will often start coding without having thought about the problem at all. Whatever they produce, it is always a mess, because they haven't planned anything, and if they eventually make it work, they will never consider "cleaning it up" as they don't even know what that means.
  5. Should you hire a tinkerer? Well, yes --- eventually they will be good. But having a team of only tinkerers is a recipe for disaster.

Excellent post! (4.00 / 3) (#21)
by tzanger on Sat Oct 28, 2000 at 09:50:28 PM EST

I myself am a pragmatist. I passed through all the stages which you've mentioned except for the #1. The tinkerer stage is difficult to get out of because you feel that your code is better.

Both pragmatic and artful code is at times hard to understand. I say this because sometimes the most efficient (time, space, etc.) code is not always the most obvious. Scott D'attalo (I think this is his name) has some routines for the PIC which simply blow me away with their efficiency. His vertical counters, debounce routines and 1% distortion sine wave generation routines simply blew me away until I finally "groked" them but now I use them in many places. Same with Microchip's math routines. Sometimes artful code is just plain weird but its elegance and efficiency are breathtaking.

On the subject of commenting, I am a firm believer in strong commenting of the code. In my PIC assembly every other line, if not every line has a comment. Subroutines have three to four lines of comments describing the routine at a minimum. If it's going to do something weird or if I've fixed something by use of some kind of hard-learned trick, I comment heavily because it's likely I'll be perusing the code later and ask "Why did I do it that way?" and make the old mistake. The comment prevents that. It's a hard discipline to get into but it is invaluable.

I'm a bit of a strange guy when it comes to indentation. My C indentation looks like this:

void func(int a, int b, char *c)
{
 int x, y;

 if(a == 5)
   {
   do_something();
   while(x++)
        {
        blah();
        }
   }

 switch(b)
       {
       case 0: booga;
               booga;
               blah;
               break;
       case 1: ooga;
               booga;
               break;
       default: error();
       }
 
 final_stuff();
}

I haven't found anyone who indents quite the same way but I have found it to be very clear and not quite as wasteful as the classic tab everywhere.



[ Parent ]
wasteful tabs? (2.00 / 1) (#33)
by mikpos on Sun Oct 29, 2000 at 01:29:14 PM EST

What's wrong with tabs being wasteful? Personally I like scopes to s-t-r-e-t-c-h-e-d out horizontally. If you've found yourself indenting more than, say, 3 times anyway, then that's a good sign that you should introduce a new function to do the inner loops (which your compiler will likely inline for you).

[ Parent ]
Tab Stretching (2.00 / 1) (#37)
by tzanger on Sun Oct 29, 2000 at 08:14:02 PM EST

What's wrong with tabs being wasteful? Personally I like scopes to s-t-r-e-t-c-h-e-d out horizontally.

That's just it; there's no technical reason. Personally I can't stand stretched out code because it's hard for me to "follow" for lack of a better word. My eyes spend too much time trying to locate the next block instead of focussing on the code.

I agree with you in the respect that if you're nesting too deeply it's likely time for another function. The large horizontal tabs are a guide in this manner but personally to me it's hard to follow.



[ Parent ]
Tab Stretching (1.00 / 1) (#38)
by tzanger on Sun Oct 29, 2000 at 08:14:02 PM EST

What's wrong with tabs being wasteful? Personally I like scopes to s-t-r-e-t-c-h-e-d out horizontally.

That's just it; there's no technical reason. Personally I can't stand stretched out code because it's hard for me to "follow" for lack of a better word. My eyes spend too much time trying to locate the next block instead of focussing on the code.

I agree with you in the respect that if you're nesting too deeply it's likely time for another function. The large horizontal tabs are a guide in this manner but personally to me it's hard to follow.



[ Parent ]
You are partly mistaken... (1.00 / 1) (#44)
by trhurler on Mon Oct 30, 2000 at 01:40:20 PM EST

All correct code is provably so. However, almost no code will ever be proven correct, because the expense of doing so, no matter how talented the programmer may have been, is outrageous. It makes buying supercomputers look cheap pretty quickly - and it will require the use of such machines, too.

As for your classification of programmers, it isn't bad, except that tinkerers are undoubtedly superior to pragmatists - neither produces code that is truly maintainable, but at least the tinkerer produces code that is excellent from a functional standpoint, and due to raw ability, usually produces more of it in less time, too.

Do not make the common mistake of assuming that the best programmers will always produce code that is excellent in every way. The best programmers produce what is needed in a given circumstance. Most times, maintainability is a paramount concern - but not always. Usually, efficiency doesn't matter - but in the cases where it does, it usually REALLY does. In general, robustness is very important, but every now and then(usually when maintainability also doesn't matter,) you really don't give a damn.

In short, you have tried to simplify a complex discipline into a set of rules that a PHB can understand, and through no fault of your own, your attempt to reduce complexity without reducing accuracy has been only partially successful.


--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
Different code for different purposes (3.16 / 6) (#13)
by katravax on Sat Oct 28, 2000 at 09:30:12 AM EST

It's pretty clear to me the balance between efficiency and readability. It's the difference between working alone and working on a team. At my office two of us are absolute speed freaks that will try anything to make a block faster and smaller, including inlining asm and using stupid syntax and/or compiler tricks to eek out the most performance we can. However, we don't pull that at work, but rather in our personal projects. One of the best compliments you can get is when another person takes over your project and they come up to you and tell you how clear your code is.

Code that is part of team code (or is owned by another, like your employer) should, based on its purpose, be readable and maintainable. I agree completely with the "readability" group on this point. But code that is just mine for personal use (i.e. home projects) is riddled with tweaks. The reason is that my employer has a base of code that must be maintainable, and I can't guarantee I'll work there forever, so it's my duty to the company to make sure my code is readable by the average programmer (including explanatory comments, sample calls, etc.). In the long run, the maintainability of the code will be more important than tweaked performance. So would I include my asm message pump in code for work? No way, though it's about 40% faster than the standard pump. Do I include it in home projects? Hell yes. The bottom line is that on team projects, maintainability trumps art.

Among some tricks I use for the best of both worlds (keeping in mind which is more important for the particular subject at hand) is that I rewrite almost everything once I have a working class or product, depending on the project. You know the old twinge of "I wish I could rewrite that based on what I've learned"? I give in to that twinge, and my code is better than ever, both in terms of performance and readability. Also, I usually include the tweaked version in comments, with the note to the future programmer that "If you understand this block of tweaked code, use it, but leave the untweaked one in comments afterward in case the next guy doesn't". On projects where that particular block needs to be sped up, I'll leave in the tweaked version with the readable version in comments and a note that if the programmer doesn't understand the tweaked part, comment it out, replace it with the part in comments, then work on it. For much longer blocks where we don't want a couple pages of comments, I'll store one version in source control and immediately store the other version after it with short notes that version x contains tweaked or readable portions of a given piece. I guess the bottom line is, do both, and based on the purpose, make the correct one default, but leave the other one completely available and easily swapped in.



Re: Different code for different purposes (4.33 / 3) (#18)
by tjb on Sat Oct 28, 2000 at 01:11:21 PM EST

Exactly.

My job, for the most part, entails writing code for a small (and slow) embedded environment (6.5K code space, 1.5K general data space, 16K hardware FIFO --- 24 MHz, 4 clock instruction cycle, no pipline). Obviously, this isn't a whole lot of code space or MIPS. Therefore, I'm forced to use C as my RAD tool while the production code is highly tuned (and marginably readable) assembly code.

Generally, when I'm adding a feature, I'll code it up and get it working in C (I may have to disable other features to do so). When I'm satisfied with it, I'll recode the functionality in assembly. Then I'll add tags to both the C and the asm to show equivalence as much as possible. That way, if myself, or someone else, needs to go back through and rethink something all they have to do is look up the C, find the tags, and search for the tag in the asm.

This isn't about worshipping the "false god of efficiency". This is about reality and limitations. If I left all my code in C, it would be well over 12K rather than the ~5K its at now and probably wouldn't run fast enough to be worth anything.

--Tim

[ Parent ]
Another embedded designer! (3.00 / 2) (#22)
by tzanger on Sat Oct 28, 2000 at 09:55:29 PM EST

This isn't about worshipping the "false god of efficiency". This is about reality and limitations. If I left all my code in C, it would be well over 12K rather than the ~5K its at now and probably wouldn't run fast enough to be worth anything.

Nothing like bit-packing data structures and squeezing things as best you can, eh? I write a lot of code for the Microchip PIC series of processors. The same problems hit these processors: limited resources and the management wants it done in C/C++ so they can understand it.

I've managed to keep the arguements to a minimum about doing it in assembly since I am a very verbose commenter. The fact that it just won't work in C is beside the point in a lot of cases. :-)

One of my designs uses a PIC16C77 and uses every peripheral available except for the parallel slave port. Running at 8MHz I believe the main loop gets no more than 115 instructions before an interrupt hits. This baby's got it all... interrupt on PORTB, PWM, CCP, I2C, async serial, timers, A/D... everything. The Microchip FAE was fairly impressed with the design. I think there's 15 bytes of code and 2 bytes of data memory not allocated. :-)

Not many non-small-processor people really understand the restraints placed on the programmer when a small chip is chosen. I myself love the challenge.



[ Parent ]
The role of language & compiler (3.71 / 7) (#19)
by Potatoswatter on Sat Oct 28, 2000 at 04:00:50 PM EST

Re paragraph 4 (and, broadly, all of it):
How the code is written determines how readable it is. How well it's compiled determines how efficient it is. If a language specifies that it will never totally eliminate that redundant variable test (as C++ compilers never eliminate function calls, even implicit ones), then writing efficient code that is readable becomes impossible. I blame any necessary ugliness of efficient code on C/C++, because it's the duty of the grammar to make it possible to write code in a beautiful way that is also useful to a compiler.

IMHO, it should be possible in the ideal programming language to specify that certain routines are to be used in certain procedures under certain conditions, as specified by the programmer's script (which is part of the source). This blurs the traditional line between source code and compiler, and I think it would radically change the way code is written for the better (although arguably make debugging harder in a lot of cases).
It should be possible, at least, to manually attach optimization hints to operations - have some syntax for adding property lists to any arbitrary operator. I can't describe how frustrated I get sometimes with the redundant additions that don't get optimized out by my compiler for some reason or another. Have you ever really taken the time to examine the machine-language output of your compiler? It can be surprising and disgusting.

Tagging operators alone is obviously an inelegant solution to the problem of unreadable manual optimizations in high-level code, but I think a combination of the first method I described, this, and a generally improved grammar could seriously beat the shit out of C (and make it possible to make its equivalent of C++ with natural extensions via the language itself, instead of changes to the compiler).

Sorry, this isn't my clearest writing ever, but I'm suffering from Weekend Syndrome - slept in, showered late, drowsy dumb all day...

myQuotient = myDividend/*myDivisorPtr; For multiple languages in the same function, see Upper/Mute in my diary! */;

example please (3.00 / 1) (#35)
by mikpos on Sun Oct 29, 2000 at 03:22:36 PM EST

It should be possible, at least, to manually attach optimization hints to operations - have some syntax for adding property lists to any arbitrary operator. I can't describe how frustrated I get sometimes with the redundant additions that don't get optimized out by my compiler for some reason or another.

Can you give an example of this please? I feel that there's a part in the back of my brain that knows what you're talking about, but nothing specific comes to mind.

TIA

[ Parent ]

an example (3.00 / 1) (#36)
by Potatoswatter on Sun Oct 29, 2000 at 07:39:42 PM EST

I don't know of any languages with anything like this (altho I only do know C++). If you tacked these features onto C (not a good idea, methinks), it might look something like this:

for(long index = 0; index < [loopInvar] 8; index++ [loopSIMD:indepTerm])
   myArray[index] [loopSIMD:indexTerm] = [loopSIMD:depTerm] index;

loopInvar signals to the compiler that the < operation is loop-invariant, and should be evaluated before the loop starts.
loopSIMD is a class of optimizations for vectorization of loops:
indepTerm hints to the processor that it should generate SIMD instructions with this ++ operation as the independent term in the loop's motion.
indexTerm tells it to use [index] as the subscript for an SIMD operation.
depTerm signals that the set operator will be using a loop-dependant term, and to optimize it as such.

The compiler frontend attaches these data to the internal representation of each operation, and the backend uses them to supplant the need for cleverness on its part.

This probably isn't the best example, but I have vector optimizations on my mind... the compiler industry seems to have hit a roadblock with them.

myQuotient = myDividend/*myDivisorPtr; For multiple languages in the same function, see Upper/Mute in my diary! */;
[ Parent ]

[loopInvar] not needed, but... (none / 0) (#42)
by mikpos on Mon Oct 30, 2000 at 08:48:26 AM EST

[loopInvar] can already be done by making things const. Even if they're not declared const, the compiler may be able to see right through them. I know gcc's -funroll-loops is actually half-decent.

As for the parallelism bit, that isn't done by gcc right now (though it's not like it would be impossible to do given the current C language). However, Cilk, which is a highly-parallelised language based on C, might be able to help you there.

I think what would be nice about C (which I guess is pretty much what you already said) is the ability to add qualifiers to a variable without having to redeclare it. e.g. if my int is 'const' during a certain part of code, or my pointer is 'restrict' during a certain part of code, trying to reflect that I would have to redeclare it (using a reference, which is especially nasty). Oh well.

[ Parent ]

woops. (none / 0) (#43)
by Potatoswatter on Mon Oct 30, 2000 at 01:39:37 PM EST

What I intended to show was that the operationis const. Currently, there's no way to tag the operation, just variables. The line should read:
   for(long index = 0; index <  myLong + [loopInvar]  8; index++ [loopSIMD:indepTerm])
since the < operation is not loop-invariant at all.

myQuotient = myDividend/*myDivisorPtr; For multiple languages in the same function, see Upper/Mute in my diary! */;
[ Parent ]
i think it would work best with variables (none / 0) (#49)
by mikpos on Mon Oct 30, 2000 at 05:48:16 PM EST

What I intended to show was that the operationis const. Currently, there's no way to tag the operation, just variables.

I think tagging the operation would be the wrong way to do it. An operation is const if its operand(s) are const. Hence, I think the best solution would be to be able to change a variable's qualifications on the fly.

Also, what would be nice would be some granularity on objects. Right now, qualifiers are like conventional Unix permissions: all or nothing. Either I can execute a program or I can't; either a variable is volatile or it isn't. Capabilities would be a bit nicer. e.g. maybe I want to be able to modify what a pointer is pointing to, but I'm not allowed to free() it. I suppose it could get really messy that way, though, and wouldn't offer much to the compiler in terms of optimisation hints.

[ Parent ]

more than just const (none / 0) (#52)
by Potatoswatter on Tue Oct 31, 2000 at 09:36:51 AM EST

There's more to tagging operations than just whether they're always const or not - you can say const relative to what loop, and (as I showed in my example) things like vectorization can be helped though hints. It's a very wide class of information that can't be passed to the compiler.

Overall, I think that tagging anything is generally inelegant, but IMHO it would work well with the other elements I suggested to be added to a C-like (but not C-derived or, necessarily, compatible) language.

I should probably mention I'm kinda working on such a language. If anyone's interested, do mail me.

myQuotient = myDividend/*myDivisorPtr; For multiple languages in the same function, see Upper/Mute in my diary! */;
[ Parent ]

C99 (none / 0) (#55)
by B'Trey on Tue Oct 31, 2000 at 07:10:14 PM EST

I'm not sure if this is exactly what you're talking about but the new C standard adds the keyword restrict. It tells the compiler that a pointer is the only method of access to a particular memory location. It doesn't affect how the code functions but it does give the compiler additional info. It's only purpose is to allow optimizations.

[ Parent ]
No conflict (3.00 / 4) (#20)
by Simon Kinahan on Sat Oct 28, 2000 at 08:27:39 PM EST

There's no real conflict between efficiency and conceptual clarity and expressive power. The idea that there is something that comes out of the implementationist/abstractionist split amongst people involved with software.

An implementationist is someone whose primary concern is with the code used to implement something. Implementationists generally have a poor grasp of architecture. When implementationists draw UML diagrams they worry obsessively about the correspondence to code structures and the diagrams come out clunky. Implementationist generally write efficient, thorogh code, and can hold a lot of state in their heads, but the boundaries between system components get messed up in implementationist hands.

An abstractionist is someone who thinks mainly in terms of the entities and relationships iin a system and worries less about the actual code. It can be hard to tell the difference between a real abstractionist and a bullshit artist. Abstractionists often write code thats a bit of a mess, but whose interfaces are sound and well thought out.

Implementationists outnumber abstractionists 10 to 1, and in most companies efficient technical discourse, especially early in a project, gets bogged down in too many implementation issues because of this. Now how does this fit in with efficiency vs abstraction ? Because where some concern with abstraction has found it was into a system it tends to appear as an obstacle from an implementationist standpoint. If the system has a performance problem and the abstract interfaces between components are contributing to the problem, an implementationist will usually end up modifying the interface to include either more details of the process in which it is being used, or more information about the component, breaking the asbtraction. Hence a belief arises that abstraction hinders efficiency.

Simon

If you disagree, post, don't moderate
aesthetics? (3.00 / 1) (#29)
by mikpos on Sun Oct 29, 2000 at 10:03:19 AM EST

Am I the only one who thinks that the beauty in some code can be in the aesthetics? Smalltalk definitely is a beautiful language in terms of concept, but it's probably the ugliest language I have ever seen in terms of aesthetics.

The aesthetics of something is the beauty of it... (none / 0) (#48)
by SIGFPE on Mon Oct 30, 2000 at 05:15:25 PM EST

...by definition of the word 'aesthetic' so maybe you need to say again what you mean!
SIGFPE
[ Parent ]
aesthetics as i meant it... (none / 0) (#53)
by mikpos on Tue Oct 31, 2000 at 06:24:45 PM EST

...is "easy on the eyes". HTH :)

[ Parent ]
Another attempt... (3.28 / 7) (#45)
by trhurler on Mon Oct 30, 2000 at 01:53:36 PM EST

to explain subtlety in terms of simplicity. They all fail.

Put simply, programming is not simple. The requirements(not functional product requirements, but rather, the things a programmer has to consider as important while doing his job, no matter which part of it,) are different every time. In truth, you have only a few good programmers, and fewer good managers, who know this fact. Everyone else is a zealot for this or that aspect, and is an annoying ass.

First, you have code size zealots. These are the lowest form of life. Do not confuse with first semester assembler students, who are merely enjoying an amusing game. There is almost never any justification for this attitude, and when there is, these guys don't know it anyway.

Then, there are speed zealots. These are better; in a few jobs, they really shine. However, most of them don't know what they're doing, and the few that do probably know enough to not appear quite so unreasonable as most of their brethren.

And, we have, most recently, maintainability zealots. These people honestly believe that no matter what, all code must be maintainable at the expense of every other consideration. They're actually the biggest idiots, because their position is the most laughably obviously false, but they're the most recent and fashionable sort of idiot, and so criticizing them tends to make most people think you're the fool. There are a variety of such zealots, such as the OO zealot, the component zealot, the strong typing zealot, and so on. They're all morons unless and until they figure out that there's more than one goal in software development.

If I'm writing code that will be around 20 years from now, then maintainability matters - unless, of course, there is a hard performance limit that can't be met without the use of something gross. If I'm writing code that will run once and be thrown into the bit bucket, then whatever generates correct results in the least amount of my time is the correct solution. If I'm writing code that will be replaced in a year, then something in the middle is called for. I could go on writing up such examples all day. The point is quite clear, though: no single process requirement always dominates. You have to decide what is important, and then you need the knowledge, experience, and (yes, I'm quite serious) raw conceptual ability to pull it off.

The zealots make good specialists. A few of them think they're masterful(this is especially true of speed, elegance, and maintainability zealots.) They're not. The few who really are are the generalists - they can do speed, they can do maintainabililty, and so on. Unfortunately, most people are never even aware of this, much less do they seek after it.


--
'God dammit, your posts make me hard.' --LilDebbie

Maintainability (none / 0) (#54)
by B'Trey on Tue Oct 31, 2000 at 06:52:23 PM EST

Anything can be taken to extremes. I fully agree that maintainability is only one of several (sometimes mutually exclusive) goals which must be considered by the programmer. So I support much of your post.

However, there is one thing I'd like to address briefly. Many people seem to think that since the world did not end at 11:59, 31 Dec 1999, the entire Y2K problem was a bunch of hype. It wasn't. There were significant problems and businesses (and governments) spent a GREAT deal of money testing and fixing code so that it didn't blow up. The lack of a crash wasn't an indication of the size of the problem; it was a measurement of the success of the solutions. Why do I bring this up? Because a lot of the problems that were fixed were in code that was meant to be "run once and then thrown away." If you're writing a utility for your own purposes on your own system, you might be safe in making the assumption that the code is disposable. But if you're being paid to write code, ALWAYS assume that it is for long term use. Even when you know that it isn't. Lots of those COBOL programmers "knew" that their code wouldn't be around in 2000. They were wrong.

[ Parent ]

I understand what you're saying... (4.00 / 1) (#56)
by trhurler on Tue Oct 31, 2000 at 08:22:31 PM EST

although I must say, it is not clear that the effort spent "cleaning up" the y2k related issues was more expensive than it would have been to try to do it right the first time. It seems obvious, but try and find any real evidence. Many of the solutions employed wouldn't have worked 20 years ago on the hardware of the day, or wouldn't have been practical. Many of the solutions amounted to "we're finally going to replace this crap," which will happen sooner or later no matter how well you write code(much later, perhaps, but no code is immortal.) Some of the problems had obvious "badness" in them and should never have happened. Some of them, obvious now, simply -weren't- obvious in the 50s or 60s. Odds are we'll have similar issues arise in the future, no matter how careful we are. Fortunately, we'll be able to fix them, because we're really good:)

Lest anyone think I'm anti-maintainability, I'm not. However, I do know that in business, economics is always the first concern; if it is actually cheaper to do it crappy the first time and fix it later, then that may well be what gets done. As long as nobody's life or property is on the line, that's probably good enough. I do it right any chance I get, and you should too, but we're not always in charge, and even if we were, there are only so many of us and so much time, and there is an amazing amount of work to be done.


--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
What constitutes maintainability? (none / 0) (#59)
by Miniluv on Thu Nov 02, 2000 at 04:03:38 AM EST

I do not write code for a living, or all that often as a hobby, so bear that in mind. I'm looking at this from the perspective of what I do for a living, that is maintain servers and networks...and principles apply from one to the other in a broad way.

Maintainability is not necessarily clean code. Usually it involves some of that, it means not writing incredibly "ugly" speed hacks, and not being ridiculously cryptic in the code structure. That's not all it means, not to me anyways.

Is really explicit code with no comments maintainable? Sure, the code is quite explicit and clear and uncluttered, but ya still have to read the whole damn set of source to figure out exactly what fits where. I think that commenting ugly code is far better than not commenting explicit code. Documentation is everyones friend, despite it being a bitch to write. Sure, most coders write shitty documentation for users...but that's because they're writing for people at a lower level of comprehension than them. I think coders can write docs that work just fine for other coders. If I write code that uses the bagel() function, and it takes one argument and returns one value, I can document that through comments, and maybe use those comments as a reference for writing a general document saying, "This was the specification for the code, here's the functions, their specs, here's the general outline, there's comments in the code for the details."

That is maintainable code, but it can be optimized and tweaked too.


"Its like someone opened my mouth and stuck a fistful of herbs in it." - Tamio Kageyama, Iron Chef 'Battle Eggplant'
[ Parent ]

phear the angry programmer! (3.66 / 3) (#61)
by 0xdeadbeef on Sat Nov 04, 2000 at 12:55:15 AM EST

Then there's the "anyone who doesn't agree with my pompous cynicism is an idiot" zealot. :-)

Do you actually write code for a living? Can you think of any example where maintainability does not enhance the other attributes, aside from the 15 minute throw away perl script?

I would argue that the only goal in software development is to achive the desired functionality with the minimum cost. All other goals fall out of that one. People don't stress maintainability out of some aesthetic sense, they do it because its the only way to keep costs down on any piece of code touched by more than one hand.

[ Parent ]
Programming is not an art (3.33 / 6) (#47)
by greyrat on Mon Oct 30, 2000 at 04:48:02 PM EST

...anymore than building a car is an art. I use that example on purpose, because one might consider designing a car to be an art although that is not really true either.
In that sense, creating algorithms is an art (just like designing a car is "art") and that "art" is independent of the method of construction.
Programming languages are methods of construction. Use the best one for the current circumstance, including what algorithms you are applying at the time.
Enough from me...</rant>
~ ~ ~
Did I actually read the article? No. No I didn't.
"Watch out for me nobbystyles, Gromit!"

The C++ Standard Template Library (3.33 / 3) (#50)
by SIGFPE on Mon Oct 30, 2000 at 07:17:37 PM EST

The STL code resides solely in header files which means we've all had a chance to look at it if we want. It's hideous code. I do a lot of algorithm development and like to make my code generic. This code often looks hideous too.

It seems to me that readability and genericity (in C++) are mutually exclusive goals.
SIGFPE
Arrrgh, I want comments! (none / 0) (#60)
by h0tr0d on Fri Nov 03, 2000 at 12:46:48 PM EST

I just want comments people!

Ugh, I just started looking at some new code (open-source) to help a friend out on a project and discovered that there are approximately two comments for every fifty lines of code. To me this is so frustrating. Comments are not difficult or time consuming to add as you write code. My company asks that we write one comment for every three lines of code. While not all follow this rule it is very helpful when it is adhered to. I have found that for the most part, if someone fails to comment their code well it is because they really do not understand what it is that they are doing. Oh well. Just needed to vent. There is nothing more frustrating than trying to figure out someone else'e assembly code without any comments.

AAAARRRRRRGGGGGGGHHHHHH!

-- It appears that my spleeing chucker isn't working again.

Comments are a crutch (none / 0) (#62)
by zerblinitzky on Fri Nov 10, 2000 at 11:04:57 PM EST

Comments, I always believed, were a crutch for people who couldn't (or chose not to) write clear code. 'Code' isn't an accurate name, ideally. Programs should be readable just as they are, with make-sense variable names and clearly written routines. I cannot count the number of times I have looked at somebody's code where they wrote it wrong, and then traced through by hand reading the comments instead of the source and swore to me that it should work! Well, no, Goober, the machine couldn't care less what the comments say, it's the code that it reads. The problem is that people think, "I'll write comments to make my code readable!" But, it doesn't make the code readable, really. It just masks the code with this big mass of comments that affects nothing, but distracts you from reading the source. Just write the code clearly, and spread it out with plenty of whitespace, and use variable names that make sense, and learn to read code like the compiler does, as a language. Then you will find that you do not need comments any more.

[ Parent ]
Resistance to "good" programming techniques | 62 comments (62 topical, 0 editorial, 0 hidden)
Display: Sort:

kuro5hin.org

[XML]
All trademarks and copyrights on this page are owned by their respective companies. The Rest 2000 - Present Kuro5hin.org Inc.
See our legalese page for copyright policies. Please also read our Privacy Policy.
Kuro5hin.org is powered by Free Software, including Apache, Perl, and Linux, The Scoop Engine that runs this site is freely available, under the terms of the GPL.
Need some help? Email help@kuro5hin.org.
My heart's the long stairs.

Powered by Scoop create account | help/FAQ | mission | links | search | IRC | YOU choose the stories!