Spartan Programming

July 8, 2008

As I grow older and wisereven older as a programmer, I've found that my personal coding style has trended heavily toward minimalism.

I was pleased, then, to find many of the coding conventions I've settled on over the last 20 years codified in Spartan programming.

300-movie.jpg

No, not that sort of Spartan, although it is historically related. The particular meaning of spartan I'm referring to is this one:

(adj) ascetic, ascetical, austere, spartan (practicing great self-denial) "Be systematically ascetic...do...something for no other reason than that you would rather not do it" - William James; "a desert nomad's austere life"; "a spartan diet"; "a spartan existence"

I've tried to code smaller, even going so far as to write no code at all when I can get away with it. Spartan programming aligns perfectly with these goals. You strive for simultaneous minimization of your code in many dimensions:

  1. Horizontal complexity. The depth of nesting of control structures.
  2. Vertical complexity. The number of lines or length of code.
  3. Token count.
  4. Character count.
  5. Parameters. The number of parameters to a routine or a generic structure.
  6. Variables.
  7. Looping instructions. The number of iterative instructions and their nesting level.
  8. Conditionals. The number of if and multiple branch switch statements.

The discipline of spartan programming means frugal use of variables:

  1. Minimize number of variables. Inline variables which are used only once. Take advantage of foreach loops.
  2. Minimize visibility of variables and other identifiers. Define variables at the smallest possible scope.
  3. Minimize accessibility of variables. Prefer the greater encapsulation of private variables.
  4. Minimize variability of variables. Strive to make variables final in Java and const in C++. Use annotations or restrictions whenever possible.
  5. Minimize lifetime of variables. Prefer ephemeral variables to longer lived ones. Avoid persistent variables such as files.
  6. Minimize names of variables. Short-lived, tightly scoped variables can use concise, terse names.
  7. Minimize use of array variables. Replace them with collections provided by your standard libraries.

It also means frugal use of control structures, with early return whenever possible. This is probably best illustrated with an actual example, starting with raw code and refactoring it using the spartan programming techniques:

I don't agree with all the rules and guidelines presented here, but I was definitely nodding along with the majority of the page. Minimalism isn't always the right choice, but it's rarely the wrong choice. You could certainly do worse than to adopt the discipline of spartan programming on your next programming project.

(hat tip to Yuval Tobias for sending this link my way)

Posted by Jeff Atwood
226 Comments

2bandini

Well, I didn't mean to take my words literally. There's nothing wrong when you are making proper use of short varibales. Certainly I use them when I need to and where it is obvious what they are for, e.g. in loops as I've already said.

What you should avoid is OVERuse. Or in different words, making most (or even nearly all) variables one-letter. I think you see what I mean.

Malcolm on July 8, 2008 1:13 PM

I simply had to comment on your post:

http://compaspascal.blogspot.com/2008/07/code-style-of-old-age-programmers.html

Lars D on July 8, 2008 1:13 PM

I think you're right on about most things, except terse variable names. For counters, sure, but for most other things, readability is key.

The problem with variable terseness is that it optimizes for *writing* code, which is optimizing the fast case. Variable names should be explanatory to optimize for the real bottleneck, *reading* (comprehending) code, which is the slow operation.

Eric Beland on July 8, 2008 1:30 PM

Let me pipe in on the subject of public variables.

First, if your language doesn't allow you to do:

object.var = expression;

where var= is a method, then maybe you should truly stay away from them, because your language sucks. :-) Well, because your language does not provide the proper support to leverage usage of the assignment operator. The resulting impossibility of changing the implementation of a getter/setter from a simple variable to something more complex is a deficiency in the language.

Anyway, EXPLICIT getters and setters are noise, not signal. If getters and setters weren't noise, then variable assignment would work like this:

x.setvalue(expression);
x.getvalue();

It doesn't, for good reason. It obscures. It makes the most simple expressions difficult to read.

For a good example of doing this right, see Scala. In scala, you define your class getters and setters methods, which are then used by the compiler to interpret expressions and assignments properly.

x stands for value of x. x = expression is an assignment (:= or variants, if you prefer). One should try to avoid using two different solutions to the same problem at the same time.

Daniel on July 8, 2008 1:33 PM

Jeff, you tell us that Code Complete is your favorite book, but this post is an obvious opposite of what is said in Code Complete.

I agree. Single letter variable names is an example of minimalism being the wrong choice, but it seems to be an important principle of Spartan Programming. It would be nice to see more information when you say you don't agree with all the rules and guidelines presented here, especially since some of the Spartan principals directly contradict what you seem to say in your earlier writing when you praise the concepts in Code Complete.

Andrew on July 8, 2008 1:51 PM

i could not help but refactor the java-example to c#:

public static void SendEmail(
string vsFrom,
Liststring vsaRecipients,
string vsSubject,
string vsBody,
Liststring vsAttachements,
bool vbActuallySend)
{
MailMessage msg = new MailMessage();
msg.From = new MailAddress(vsFrom);
foreach (string lsRecipient in vsaRecipients)
{
msg.To.Add(new MailAddress(lsRecipient));
}
msg.Subject = vsSubject;
msg.Body = vsBody;

foreach (string lsFilename in vsAttachements)
{
if (!File.Exists(lsFilename))
{
throw new FileNotFoundException(file not found, lsFilename);
}
msg.Attachments.Add(new Attachment(lsFilename));
}

if (vbActuallySend)
{
SmtpClient loSmtp = new SmtpClient();
loSmtp.Credentials = null; // insert your creds here
loSmtp.Send(msg);
}
}

It has not the exact same function as the example, but seems to be more readable, i hope. Of course, some of the won readability is the merit of the sophisticated .NET-libs, not the language itself.
The function itself is possibly superfluous, because you could rebuild this scheme at the position of the function-call - could be more mantainable in some cases.

titrat on July 9, 2008 2:21 AM

Hi,

I guess I am responsible for coining the term Spartan Programming, and for authoring the wikipages on the topic. My attention was drawn to this thread. I truly appreciate the comments, and would like to hear more of those - and get the discussion going.

Here is my response to some of the comments I saw here:

- Terse naming in in a sense an end, not a tool. That is, you should code in such a fashion that terse names make sense. Consider for example the code fragement: c.setSN(f.getSN()); used here.
Is c a good or a bad name? Well, it depends, not on the complexity fo the algorithm, but rather on the scope: if c is defined and used in a very small scope (say a handful of lines), with a few other similarly short scoped identifiers hanging around, then c is an ideal name.

If however, the scope is large, and there are many other names that the reader has to keep in mind, then c is a bad name. The cure I think is not in using a longer, more meaningful and much more verbose
name for this entity, but rather in refactoring that would make the code small enough so that c becomes an appropriate choice.

- There were some comments about foreach construct. I firmly believe that forach is the way to iterate, whenever possible, but
not beyond that limit.

- PHP can also be spartanized: I once run a huge project in which I was trying to semi-automatically spartanize a CMS program, using the number of conditional as the primary metric. The results were encouraging, but the whole project went down the drain due to a disk crash.


- Please note that the wiki offers many examples of Java classes written in the Spartan style. These are not perfect, but you may want to consider these in your discussion. Here are a number of links
(there are more):

http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/Class_Position
(an implementation of a (line:column) pair

http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/Class_Graph
(an implementation of a Graph data structure)


http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/Class_DBC
(an implementation of Design by Contract)

http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/Class_Defaults
(a useful micro abstraction)

Many more examples can be found by searching for the Class XXXX pattern in the wiki index.
http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/Special:Allpages

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

Thanks for your attention, I would try to make myself available for more comments and discussion.

Yossi

Yossi GIl on July 9, 2008 2:22 AM

I actually agree with most of Graham's comments about C. For bare metal work, I think you are actually still better off using Ada if you know it and have a compiler available, but those are two huge ifs. That's another argument for another time though.

/most/ of those techniques sound like they will work just fine in C. List the ones you think are impossible or difficult.

Actually, that's a very good suggestion.
Using the lists from http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/Spartan_programming

Frugal use of Variables
1) The first half of this works great in C, the second half doesn't work at all. C has no such advanced programming constructs.
2) This works in C today, although it is only recently that compilers started allowing variables to be defined in loops.
3) No such thing as a private variable in C. If its in the interface, anyone can whack at it.
4) No such thing as constants in C. You can kind of fake it with the macro preprocessor, but that's not type-safe, has universal scope, and is just plain nasty to boot. No restrictions. No annotations.
5) Not really sure what this one means. But I am sure it works just fine in C, whatever it is.
6) This one's a bit vague too, but as I understand it, it should work just fine in C.
7) No standard componet library for C. I'm sure there are tons of do-it-yourself C-only component libraries floating around, but none of them are really universally recognized standards. Every program I've seen rolls its own data structures using pointers or arrays.

Frugal use of Control
1) No classes or inheritance support.
2) Works fine in C.
3) No classes, thus no action applicator classes.
4) Works better in C than most other languages I've seen. Ada allows you to label loops and exit out of specfic layers, instead of just one, but Ada doesn't have continue.
5) Designed for C, obviously.

T.E.D. on July 9, 2008 2:22 AM

Dear Jeff,
someone is eating all trailing whitespace in the comments - is there a special reason for this behaviour?

titrat on July 9, 2008 2:25 AM

Following the discussion, I completed the Java Spartan Programming example. The result, shown at http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/SendAnEmail_case_study

demonstates why short, one variable names makes sense.

private static void sendEmail(final String from, final ListString to, final String subject, final String body,
final ListFile attachments) throws Throwable {
final Session s = Session.getInstance(new Properties());
final Message m = new MimeMessage(s);
setHeaders(m, from, to, subject);
setContent(m, body, attachments);
sendMessage(m, s);
}

private static void setHeaders(final Message m, final String from, final ListString to, final String subject) {
m.setSentDate(new Date());
m.setFrom(new InternetAddress(from));
for (final String t : to)
m.addRecipients(Message.RecipientType.TO, InternetAddress.parse(t, false));
m.setSubject(subject);
}

private static void setContent(final Message m, final String body, final ListFile attachments) throws MessagingException {
if (attachments.size() == 0) { // ''No attachments to send''
m.setText(body);
return;
}
// ''Generate a multi-part message''
final MimeBodyPart textPart = new MimeBodyPart();
textPart.setText(body);
final Multipart mp = new MimeMultipart();
mp.addBodyPart(textPart);
for (final File f : attachments)
mp.addBodyPart(makeBodyPart(f));
m.setContent(mp);
}

private static MimeBodyPart makeBodyPart(final File f) throws MessagingException {
final MimeBodyPart $ = new MimeBodyPart();
final FileDataSource fds = new FileDataSource(f);
$.setDataHandler(new DataHandler(fds));
$.setFileName(fds.getName());
return $;
}

private static void sendMessage(final Message m, final Session s) {
final Transport t = s.getTransport(smtp);
t.connect(smtpServer, port, userName, password);
t.sendMessage(m, m.getAllRecipients());
t.close();
}

Yossi GIl on July 9, 2008 2:38 AM

@J. Stoever:
..[my code].. That's terrible, terrible, terrible code. The entire logic is removed and abstracted away in favor of some bit juggling that nobody is ever going to understand again six months from now.

Hmm... there is absolutely NO bit juggling going on there. So I'm not sure what you mean by that.
Personally I think it's purpose is pretty obvious and I've never had comments on code like that from peer-reviewers or guys doing maintenance.

But okay, if you want the logic to be more explicit then you could also have the slightly more verbose:

bool statusOk = func1();
if (statusOk)
____statusOk = func2();
if (statusOk)
____statusOk = func3();

if ( statusOk )
____dostuff();

return statusOk;

..which likewise doesn't suffer from the need to indent 20 times within a function, which as my original point.

If you want to see a properly obscure way of doing this, then consider this approach which was suggested to me in the past (and rejected):

bool result = false;

do {
____if (func1() == false)
________break;
____if (func2() == false)
________break;
____if (func3() == false)
________break;

____dostuff();
} while (false);

return result;

Graham Stewart on July 9, 2008 3:02 AM

Terrible idea. Oh, I can see its use in math formulas where the Xs and Ys are established by convention, but parameters as single letters? Never!

int abs(int x) {
if (x 0)
return -x;
else
return x;
}

is clearer than

int abs(int x) {
if (x 0)
return -x;
return x;
}

Rather than frugal coding, I suggest a frugal response. If I were to write one, it would likely be:

!me

Paul Schirf on July 9, 2008 3:11 AM

to Schirf: how about:
int abs(int x)
{
return (x 0) ? -x : x;
}

at least it's spartanic ...

titrat on July 9, 2008 3:26 AM

Weird philosophy!

Your basically saying why use existing, well-tested language constructs and libraries that hide your code when you can write your poor equivalents with new and interesting bugs.

Yeah... I take it too far. Still... I will use stuff like strcpy etc out of laziness... but to use a whole framework like .net or java... no thanks. Only when a project gets really big... and as a one man band that basically never happens. :)

Jheriko on July 9, 2008 3:37 AM

Yes, a little bit cryptic, single-letter identifiers whenever we need, for *we do know what the asceticism is*.

Considering the article on regular expressions, I am afraid I am not able to get the point. Readable and maintainable code? I don't think so ...

Maximus on July 9, 2008 3:42 AM

To me, a variable name can never be too long. In my opinion, a variable name absolutely has to be descriptive, so that other programmers can more easily see what is going on. If a variable must be named horizontalScalingFactorBeforeZoom, then so be it.
This is what led to me spending half, before realising that horizontalScalingFactorBeforeZoom and horizontalScaleFactorBeforeZoom weren't the same. Compare scale and scaling.
LONGER IS NOT MORE DESCRIPTIVE.

Tom on July 9, 2008 3:51 AM

To me, a variable name can never be too long. In my opinion, a variable name absolutely has to be descriptive, so that other programmers can more easily see what is going on. If a variable must be named horizontalScalingFactorBeforeZoom, then so be it.
This is what led to me spending half an hour debugging some code, before realising that horizontalScalingFactorBeforeZoom and horizontalScaleFactorBeforeZoom weren't the same. Compare scale and scaling.
LONGER IS NOT MORE DESCRIPTIVE.

Tom on July 9, 2008 3:52 AM

I suppose its best with an example... by using the C++ string functions to extract values from plain text (sscanf) and do stuff with them (strcat/sprintf) I had a particular app taking minutes to parse large text files. My friend suggested that the string functions were at fault, which I initially was pretty skeptical about, but I debugged the code and stepped through to the runtime library code where I found that strcat was needlessly rechecking the string length (I could have worked this out for myself on inspection were I a better programmer).

So I wrote my own code that kept track of the length from the beginning and increased it as it created the string... problem solved. It became unnoticably quick... i.e. it took less than a second to parse the file and produce the new text.

there is a strncat function... but sadly it provides the rather useless option of adding the first n chars. rather than letting you feed the length of the string to be appended to. mystr[n] = 0; is any harder to read or understand than strncat(foo, mystr, n)?

API and language developers are just as crap as the rest of us.

I'm sure this is a pretty noobie problem that many may have encountered. I'd be interested to hear if there is a good solution using pre-written functions...

This is precisely the sort of thing which .NET will hide under a foreach or similar construct too...

Jheriko on July 9, 2008 3:53 AM

I agree, on the whole, with this post and with Graham. All too often I see:

if(hasBrain == true)
return true;
else
return false;

Firstly, as a programmer you know that hasBrain is a boolean, so why compare it to a literal value that has to be created just for the comparison? Secondly, why create a further literal for the return value, when the value already exists? I know that some people find it easier to read but, if you have two brain cells to rub together, it's just as easy to read:

return hasBrain;

This principle can also be extended to:

if(length 10 length 20)
return true;
else
return false;

Why not just:

return length 10 length 20;

I know it's (slightly) less readable but only someone with hasBrain = false won't know what it returns.

Steven Bey on July 9, 2008 4:01 AM

I think one of the more important types of minimalism these days is dependencies. This is a layer above the more code-centric issues that you have already mentioned, and looks at the system as a whole.

The other day I updated my copy of Pidgin through the MacPorts system on OS X. It worked for literally hours, downloading and building package after package of random dependencies, of which I knew nothing of their purpose. This kind of runaway dependency bloat afflicts many open source programs.

I like to build programs that depend on as little external stuff as possible. Sometimes I might have to build a tiny bit of extra functionality that could be obtained by linking with yet another library, but I choose to make it easy on the author, the maintainers, and the users.

Greg Hewgill on July 9, 2008 4:13 AM

@michael:
Of course in Python the for loop IS a foreach loop and nobody cares about performance since it's already interpreted.

Please get your facts right. First point : Python is byte-compiled (at least CPython, StacklessPython, Jython and IronPython are). Second point: yes, there are people that actually care about Python's performances.

bruno on July 9, 2008 4:16 AM

@Daanish Rumani, I've always preferred to put 'change' comments in source control rather than litter my code with them, otherwise the code just gets harder to read. That way a diff between versions, along with the comment helps to identify and cross reference any changes.

Agree with you about the braces though, I like to see them even for single line blocks, much easier to read.

j450n on July 9, 2008 4:17 AM

I think spartan is important, I code not organized and I face problems when I want to change my code :(

Omar Abid on July 9, 2008 4:49 AM

Baited by a troll? Perhaps.

Sorry @Yossi, I disagree 100%, but then I use languages with objects as first class citizens, and most of your one-liners can be removed by using a class and better use of object oriented techniques.

/// xmldoc comments deleted.
public SendMessageResult SendMessage( HtmlMailMessage htmlMailMessage)
{
// check preconditions
if( htmlMailMessage == null ) throw new ArgumentNullException(htmlMailMessage);
if( !htmlMailMessage.HasRecipients() ) throw new Exception(no recipients in message);
if( !htmlMailMessage.HasSender() ) throw new Exception(no sender specified);

// create SmtpMessage from our own HtmlMailMessage
SmtpMessage message = new SmtpMessage();

message.Type = MessageType.Html;
message.Body = htmlMailMessage.HtmlBody;
message.Sender = CreateSmtpSenderFor(htmlMailMessage.Sender);
SetMessageRecipientsTo(message,htmlMailMessage.Recipients);

try
{
SmtpClient client = new SmtpClient(mail.mycompany.com);
SentMessageIdentifier messageId = client.Send(message);
return new SuccessSendMessageResult(messageId);
}
catch( SmtpClientException ex )
{
// this method does not propagate smtp client exceptions.
return new ExceptionSendMessageResult(ex);
}
}

None of this rubbish with s, m, t, etc. It's readable even if you don't know C#, and even if you don't know what the SendMessageResult, ExceptionSendMessageResult, SuccessSendMessageResult or HtmlMailMessage objects look like, and you don't have to look at the methods CreateSmtpSenderFor and SetMessageRecipientsTo to figure out what they're supposed to do.

Maybe I've evolved, but I have to keep scanning up to see what s, m, and t are. My brain does a great job of pattern recognition, and there's a cognitive penalty to single character variable names when they're not universally accepted e.g. 'ex' is accepted for exception (in C#), but s and m are not session (or was that sender) and message universally. It's easier to read if the variable is session, not s.

The only thing I see is an attempt to round up a few half-baked refactoring techniques in a few pages of text and label it 'spartan'. You can only get out what you've put in. Read Fowlers Refactoring book, which I might add will take a few days, as it's more than a few hundred pages.

Go to http://www.refactoring.com/

RedGreen on July 9, 2008 5:28 AM

@TED I'd reckon that child.Surname = father.Surname is a little clearer.

For property-disabled languages, child.set_Surname( father.get_Surname() ); (or whatever your language's convention is)

child.surname() by itself implies that surname is an action, and is not just setting a property. I surname() you? Huh? Property get and set's are best done with a convention that implies they are just properties, and their only side-effect is a property set (or get).

Maybe to a western, english reader's perspective it's obvious, but even we can't agree what to call it.

RedGreen on July 9, 2008 5:31 AM

Oh, I didn't say that you claimed it as your own. However, you are reaping a significant benefit out of its use, which in the form you apply it is nearly a word for word copy.

Basically, the concept is like this. Suppose someone quoted 90% of a novel, interjecting comments every few pages and copiously mentioning the title and author of the novel. It would still be plagiarism and unfair use because the original content dramatically outweighs the new contribution.

The only place I did misspeak is that it's possible the wiki page has licensing that allows this type of use. Which means it is not exactly wrong. But still, the central point is that you are here deriving benefit from content which is primarily someone else's work. And I don't like it.

Your article here is barely better than a link to the page along with a picture and your disclaimer in the final paragraph. You're free to do what you please with this, but since you provide a forum for comments, I felt I should voice my objection.

James A. on July 9, 2008 5:32 AM

Bothered to look up the definition of plagiarism: I used it incorrectly. For that I am sorry. However! I still believe the use you put this wiki article to is unfair because, whether you claim _academic_ credit for it or not, you are still deriving _financial_ benefit from the work of another. Mere citation does not absolve you of this; original content would.

James A. on July 9, 2008 5:38 AM

I figured modern compilers are usually smarter than the programmer writing them. So, in all likelihood, it's better to write clear and concise code and let the compiler sort out the optimization. But, in the interest of science, the compiled bytecode is different (for now). Although the JIT'er is free to do it's own optimizations.

Typically I'd prefer code in Cone_A because it's more easily debugged and easier to understand that it's been coded correctly.

public class Cone_A
{
public double Radius { get; set; }
public double Height { get; set; }
public double ComputeVolume()
{
double result = Math.PI * Math.Pow(Radius, 3) * Height;
result /= 3.0;
return result;
}
}
public class Cone_B
{
public double Radius { get; set; }
public double Height { get; set; }
public double ComputeVolume()
{
return ( Math.PI * Math.Pow( Radius, 3) * Height) / (3.0);
}
}

The result, Cone_B::ComputeVolume() is much smaller in release mode. Execution time wasn't tested.

I apologise for the long post, but it's the only way to shut people up.

//In .net, DEBUG version of code. Cone_A::ComputeVolume
.method public hidebysig instance float64
ComputeVolume() cil managed
{
// Code size 57 (0x39)
.maxstack 3
.locals init ([0] float64 result,
[1] float64 CS$1$0000)
IL_0000: nop
IL_0001: ldc.r8 3.1415926535897931
IL_000a: ldarg.0
IL_000b: call instance float64 Inlining.Cone_A::get_Radius()
IL_0010: ldc.r8 3.
IL_0019: call float64 [mscorlib]System.Math::Pow(float64,
float64)
IL_001e: mul
IL_001f: ldarg.0
IL_0020: call instance float64 Inlining.Cone_A::get_Height()
IL_0025: mul
IL_0026: stloc.0
IL_0027: ldloc.0
IL_0028: ldc.r8 3.
IL_0031: div
IL_0032: stloc.0
IL_0033: ldloc.0
IL_0034: stloc.1
IL_0035: br.s IL_0037
IL_0037: ldloc.1
IL_0038: ret
} // end of method Cone_A::ComputeVolume

//DEBUG Cone_B::ComputeVolume
.method public hidebysig instance float64
ComputeVolume() cil managed
{
// Code size 53 (0x35)
.maxstack 3
.locals init ([0] float64 CS$1$0000)
IL_0000: nop
IL_0001: ldc.r8 3.1415926535897931
IL_000a: ldarg.0
IL_000b: call instance float64 Inlining.Cone_B::get_Radius()
IL_0010: ldc.r8 3.
IL_0019: call float64 [mscorlib]System.Math::Pow(float64,
float64)
IL_001e: mul
IL_001f: ldarg.0
IL_0020: call instance float64 Inlining.Cone_B::get_Height()
IL_0025: mul
IL_0026: ldc.r8 3.
IL_002f: div
IL_0030: stloc.0
IL_0031: br.s IL_0033
IL_0033: ldloc.0
IL_0034: ret
} // end of method Cone_B::ComputeVolume


// And again, in RELEASE mode. Cone_A::ComputeVolume
.method public hidebysig instance float64
ComputeVolume() cil managed
{
// Code size 52 (0x34)
.maxstack 3
.locals init ([0] float64 result)
IL_0000: ldc.r8 3.1415926535897931
IL_0009: ldarg.0
IL_000a: call instance float64 Inlining.Cone_A::get_Radius()
IL_000f: ldc.r8 3.
IL_0018: call float64 [mscorlib]System.Math::Pow(float64,
float64)
IL_001d: mul
IL_001e: ldarg.0
IL_001f: call instance float64 Inlining.Cone_A::get_Height()
IL_0024: mul
IL_0025: stloc.0
IL_0026: ldloc.0
IL_0027: ldc.r8 3.
IL_0030: div
IL_0031: stloc.0
IL_0032: ldloc.0
IL_0033: ret
} // end of method Cone_A::ComputeVolume

// RELEASE Cone_B::ComputeVolume
.method public hidebysig instance float64
ComputeVolume() cil managed
{
// Code size 48 (0x30)
.maxstack 8
IL_0000: ldc.r8 3.1415926535897931
IL_0009: ldarg.0
IL_000a: call instance float64 Inlining.Cone_B::get_Radius()
IL_000f: ldc.r8 3.
IL_0018: call float64 [mscorlib]System.Math::Pow(float64,
float64)
IL_001d: mul
IL_001e: ldarg.0
IL_001f: call instance float64 Inlining.Cone_B::get_Height()
IL_0024: mul
IL_0025: ldc.r8 3.
IL_002e: div
IL_002f: ret
} // end of method Cone_B::ComputeVolume

RedGreen on July 9, 2008 5:40 AM

@Joe:

Your example is exactly why I don't like early returns.
Four different return points - yikes! I definitely don't find that easier to read.

Comparisons aren't free, you loose some milliseconds on them.

Granted, but measure it. Really. Call it a ten thousand times and measure it.

Try comparing:

bool statusOk = func1();
if (statusOk)
____statusOk = func2();
if (statusOk)
____statusOk = func3();

if ( statusOk )
____dostuff();

return statusOk;

...to the original..

if (func1() == false)
____return false;
if (func2() == false)
____return false;
if (func3() == false)
____return false;

dostuff();
return true;

My version has 3 comparisons - the early return version has 3 comparisons.
The only advantage of the early return version is that it is quicker when hits an error, but the normal success path will take roughly the same time for both of them.


@T.E.D.: Thanks for that. I confess I've had one too many beers to go through it tonight, but I'll prepare a suitable rebuff tomorrow.

Graham Stewart on July 9, 2008 5:41 AM

For me, the example under 'horizontal complexity' is terrible; I always try to write such constructs as:

int abs(int x) {
return x 0 ? -x : x;
}

The shorter the code, the less the cognitive strain, the less chance of bugs, etc. I became a far more efficient coder once I started making full use of the ternary operator.

bobby on July 9, 2008 6:27 AM

@Steven Bey

You mean only someone with !hasBrain, of course ;)

bobby on July 9, 2008 6:29 AM

Here's some succinct code I wrote just now that caused a stackoverflow :)

public double LargestYVal
{
get{ return LargestYVal;}
}

An unusual retrograde fitting scenario (short version legacy code pre the coding standard in our team) caused me to use a not ideal naming convention (the private member was already labelled LargestYValue ;) ) Which meant intelisense ( a tool to aid lazy programming) allowed me to inadvertantly code a tight loop, which the compiler swallowed without complaint.

So in my attempt to avoid renaming all occurances of LargestYValue to largestYValue (to save time Ha!), I lost having to go through a second build and test !

HollyStyles on July 9, 2008 7:00 AM

The shorter the code, the less the cognitive strain, the less chance of bugs
Not really. Code that's been deliberately compressed in terms of the lines or the speed requires more cognitive strain than slightly more verbose code that performs in an easily understood manner.

Tom on July 9, 2008 7:07 AM

@bobby

I like your comment but it was a conscious decision to write it as hasBrain = false (for the guys who can't read !hasBrain), although my point was muted, slightly, as it should really have been hasBrain == false.

Steven Bey on July 9, 2008 7:17 AM

I'll give a second to this response:

Character count should be a concern, but not at the expense of readability. Ever.
--Kris on July 8, 2008 01:03 PM

N on July 9, 2008 7:22 AM

I'm the only one to think that by replacing this

String curr = i.next();
InternetAddress[] temp = InternetAddress.parse(curr, false);
toAddresses.addAll(Arrays.asList(temp));

whit this:

toAddresses.addAll(Arrays.asList(InternetAddress.parse(r, false)));

you make the code 10x less readable?

And many of rules from Spartan programming remove readability of the code?

Igor Ghisi on July 9, 2008 7:31 AM

Yup, this pretty much looks exactly like my own personal coding philosiphy.

At this moment I'm in the process of rewriting some code that isn't working. I spent weeks trying to figure out what it was doing. I traced the main execution path on a whiteboard, and found it goes through 10 layers of function calls, including a callback (but not including possible extra layers from one recursive routine). The source file is over 10,000 lines long. But if I insert a return statement at the top of the first routine there is no difference in results.

So far I've rewritten the equivalent of the first 5 layers (including the callback) in one routine that takes about 80 lines (comments included). That's actually still a bit large for my tastes, but a major improvement. I may find more I can remove later.

T.E.D. on July 9, 2008 7:36 AM

Spartan comment.

A. Lloyd Flanagan on July 9, 2008 7:43 AM

@Tom
LONGER IS NOT MORE DESCRIPTIVE

Your example shows two _long_ poorly named variables. I'd wager to say that two _short_ poorly named variables are just as bad.

A longer, more clear name is always better than something short and cryptic, but both are going to fall apart when named almost identically.

So, anyhow, yes - longer is more descriptive.

HB on July 9, 2008 7:49 AM

I think the point is that going with either too terse or too verbose causes problems. Avoid extremism, be a moderate coder.

Jeff Davis on July 9, 2008 7:51 AM

c.setSN(f.getSN());

is not as easy to understand as

child.setSurname(father.getSurname());

I'd agree with that. However, I find the following even better:

child.set(father.surname());

or in the case where there might be multiple different kinds of names a child has that you'd want to set this way (likely in this case):

child.surname(father.surname());

This first is still clearer if you can pull it off though. I think its because you can get away with noun names for constant (non-modifying) methods, but should really have verb-ish names for modifying ones. Having multiple members with the same type kind of hoses you here. This is why you should make your code as strongly-typed as is reasonable to do. Add that to the list. :-)

T.E.D. on July 9, 2008 8:00 AM

Applying Spartan techiniques during maintenence of non-Spartan code looks exactly like the movie.

David B on July 9, 2008 8:08 AM

Doesn't seem to be much of anything in the C code. They just defined a struct that needed to be defined anyway, made an enum to replace #defs, and collected two bits of common code together into a function, all of which are things they teach in first year computer science classes anyway.

Personally, I wouldn't even use the enum, since it looks like the meanings of those #defines are tied in with the hardware (I could be wrong), and #defs make it explicitly clear what the flags are, whereas the enum hides it. Enums are best used when the actual values of the enum isn't important.

Bill on July 9, 2008 8:18 AM

Hmmm. Looking over their example at http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/Spartan_programming_C_example I see one further thing I'd do. Namely, I'd put the whole frigging thing in a namespace named SAL, and then remove the SAL_ off the front of everything. I count 52 of them that would get rid of.

T.E.D. on July 9, 2008 8:33 AM

ANSI-C to T.E.D. What's a namespace?

Graham Stewart on July 9, 2008 8:48 AM

ANSI-C to T.E.D. What's a namespace?

Oooh, that is plain C, isn't it?

This is a good illustration why I always avoid C when possible. It just lacks *way* too many tools.

Add avoiding C to the list. :-)

T.E.D. on July 9, 2008 8:53 AM

To elaborate on the general unfitness of plain old C, I just went through their list of Spartan techniques. Of the variable techniques, 4 of the 7 are either impossible or much more difficult in C. 2 more are debatable. Of the 5 listed control techniques, 2 are impossible in C.

I think this is a nice objective illustration of the inferiority of C for writing concise readable code. (Although really I find C's worst offense to be lack of exception support, and the oceans of error-checking code that engenders).

T.E.D. on July 9, 2008 9:53 AM

@j450n
Our condition is a bit different. We have different teams starting with say v1.0 code base and do modifications to their modules. Each of the teams maintain a separate fork on the source control server. So when v2.0 is nearing release, there is a big bang merge that happens which would be the v2.0 code base. We then integration test it.

But apart from a big bang merge we may also have to merge code for a particular Change Request (that fixes a particular bug). And many a times, two or more change requests cause changes to the same file(s).

Besides, having the code change comments means that any person from the team can do the merge. That person receives a list of changed files, compare those with the checked-in ones, merges them locally and then they are finally check back in.

However, I do admit that it becomes messy after some time. But perhaps that is the price we pay!

Daanish Rumani on July 9, 2008 10:04 AM

Bill: You are very right. The C example is basic. It must be revised!
-------------------------------------------------------------

James A: As the author of the page on Spartan programming, I do not feel being treated too unfairly. To the contrary...
************************************
Further, what I would like to do is open the wiki for editing by
other people interested in the technique. If you are interested, please write to me (my address is not hard to find: look for Yossi Gil Technion at Google and it will give your all sorts of contact information.
------------------------------------------
----------------------------------------------------------------

RedGreen: I am not sure I agree with the criterion you offer: that the code is readable even to somone who does not know the language.
I suggest you examine the final code:
The main routine, sendEmail, has only six statements, which make the algorithm particularly clear.

final ListFile attachments) throws Throwable {
final Session s = Session.getInstance(new Properties());
final Message m = new MimeMessage(s);
setHeaders(m, from, to, subject);
setContent(m, body, attachments);
sendMessage(m, s);
----------------------------------
Paul Schirf: Liked your !me answer. And, I can understand why some people object to one letter names. Now, I wonder whether your sentiments are as negative towards the final version of the revised
sendEmail message, namely, http://ssdl-wiki.cs.technion.ac.il/wiki/index.php/SendAnEmail_case_study#Final_Result
------------------------------
Tirat: I would write the abs example as
int abs(int x) {
return x 0 ? x : -x;
}
That is, check first for positiveness (in accordance with the rule of
shortest branch first), and elminate the parenthesis.
---------------------------------------------------
T.E.D. : Would you like to help in revising the C example? Would you mind if I used your suggestions?

Yossi GIl on July 9, 2008 10:23 AM

Is there any way to make the number of commenters, and the comments, more Spartan?

Never fails -- do a post on programming preferences, watch the comments fly...

steve on July 9, 2008 10:53 AM

@T.E.D.:

Nice troll against C. I'll bite.

Firstly, I disagree - /most/ of those techniques sound like they will work just fine in C. List the ones you think are impossible or difficult.

Secondly, yeah C is a pretty old language that was never intended for modern large-scale enterprisey business solutions. So it lacks modern comfort-blankets like namespaces, exceptions, strings, classes, ADTs and a usable framework.

However it is still an excellent language for embedded and other close-to-the-metal applications where speed and size still matter.

And it is *definitely* possible to write concise C code - as for readable... well that depends who is reading it :)

Graham Stewart on July 9, 2008 11:00 AM

More comments:

* Terse naming refers mostly to variables. So in referring to the example, c.setFN(...), Spartan does not argue that the function is named setFN.

* The C example is not very convincing, I agree. Will try to find a more illustrative one.

* Most importantly, I invite anyone to examine the Spartant code samples (taken from a large, production application), to make more concrete comments.

Yossi GIl on July 9, 2008 11:26 AM

Can everyone stop? Quit sending code snippets?

PLEASE BE SPARTAN IN YOUR COMMENTS.

steve on July 9, 2008 11:38 AM

I can't remember ever disagreeing more with Jeff on any issue before.

@Graham:
Comparisons aren't free, you loose some milliseconds on them. And hiding logical sequencing using and/or tricks when calling your functions and lazy evaluation is bad taste in most languages.

Early returns are a definitive yes if your code is in a critical procedure to be called hundreds or thousands of time per second by other parts of your whole program.

It's definitely safer and faster (and easier to read) to do this:
my proc{
do_stuff();
if(error){
return error_code;
}

do_more_stuff();
if(final_condition){
return calculated_value;

even_more_stuff();
if(error){
return another_error_code;
}
return another_value;
}

than using boolean flag variables and doing comparisons all the way down:
my proc{
do_stuff();
if(error){
error_detected = true;
return_value = error_code;
}

if(!error_detected){
do_more_stuff();
if(final_condition){
final_condition_reached = true;
return_value = calculated_value;
}
}
if(!(error_detected or final_condition_reached){
even_more_stuff();
if(error){
return another_error_code;
}
return another_value;
}
}

Early return is equivalent to a GOTO, and for good reasons!!!
The Linux kernel code still has some goto's here and there, mainly on critical error catching stuff. And it's logical: if an error has been detected, you'd better go fast as fast as possible to the code that deals with the problem.

I just *hate* single character variable names, they obscure too much the code.

And I have learned to avoid ternary operators as plague over the years. Doing simple stuff like:
int abs(int x) {
return x 0 ? -x : x;
}

Is OK. But time after time in different companies I've worked for, I find developers who abuse them. I once found code written by some clever developer who thought using 4 ternary operators (2 of them nested within the other and yet with another ternary expression within) was preferable to the long version using plain old if...else structures.

Guess what? His code had bugs. And it was easier to understand/debug when I rewrote it into several if...else lines.
So I for one am happy some modern languages (like Python) have simply proscribed them. These kind of features (like inheritance and polimorphism) tend to be abused way to often.

Joe on July 9, 2008 12:01 PM

I can't remember ever disagreeing more with Jeff on any issue before.

@Graham:
Comparisons aren't free, you loose some milliseconds on them. And hiding logical sequencing using and/or tricks when calling your functions and lazy evaluation is bad taste in most languages.

Early returns are a definitive yes if your code is in a critical procedure to be called hundreds or thousands of time per second by other parts of your whole program.

It's definitely safer and faster (and easier to read) to do this:
my proc{
do_stuff();
if(error){
return error_code;
}

do_more_stuff();
if(final_condition){
return calculated_value;

even_more_stuff();
if(error){
return another_error_code;
}
return another_value;
}

than using boolean flag variables and doing comparisons all the way down:
my proc{
do_stuff();
if(error){
error_detected = true;
return_value = error_code;
}

if(!error_detected){
do_more_stuff();
if(final_condition){
final_condition_reached = true;
return_value = calculated_value;
}
}
if(!(error_detected or final_condition_reached){
even_more_stuff();
if(error){
return another_error_code;
}
return another_value;
}
}

Early return is equivalent to a GOTO, and for good reasons!!!
The Linux kernel code still has some goto's here and there, mainly on critical error catching stuff. And it's logical: if an error has been detected, you'd better go fast as fast as possible to the code that deals with the problem.

I just *hate* single character variable names, they obscure too much the code.

And I have learned to avoid ternary operators as plague over the years. Doing simple stuff like:
int abs(int x) {
return x lt; 0 ? -x : x;
}

Is OK. But time after time in different companies I've worked for, I find developers who abuse them. I once found code written by some clever developer who thought using 4 ternary operators (2 of them nested within the other and yet with another ternary expression within) was preferable to the long version using plain old if...else structures.

Guess what? His code had bugs. And it was easier to understand/debug when I rewrote it into several if...else lines.
So I for one am happy some modern languages (like Python) have simply proscribed them. These kind of features (like inheritance and polimorphism) tend to be abused way to often.

Joe on July 9, 2008 12:06 PM

I'll make my own programming paradigm. I'll call it Williamprogramming, and it's precept is one:
1) Use good programming practices.

It'll make a million dollars. Anyone want to buy my book?

Bill on July 9, 2008 12:06 PM

Jheriko: I am not sure that a programming language is just a tool in the same way a painter's brush is a tool. Your statement, poor workmen blame their tools. the opposite is also true, a master craftsmen will take the worst raw materials and the worst tools, and still will create a masterpiece may apply in the case of a painter, but not so much in the case of a program [ as the final product ] and the programming language it is written in [ as the tool ].

Here we are looking at, lets say a dish and its ingredients. Bad ingredients more than likely will result in bad tasting food, no matter how good the cook is.

For instance [ hoping not to come across as comparing apples and oranges ], how would you separate slowness of a program written in, lets say, Perl from the language when compared to its equivalent written in a compiled language, C or C++.

Languages are more than just a tool in that they give life to algorithms while carrying on their own built-in attributes.

Ivan on July 9, 2008 12:25 PM

@Graham Stewart

I haven't laughed so hard in a long time ....


bool result = false;

do {
____if (func1() == false)
________break;
____if (func2() == false)
________break;
____if (func3() == false)
________break;

____dostuff();
} while (false);

return result;

Chris on July 9, 2008 12:41 PM

I hope Spartan Programming does not mean converting this:
if(bIsAlive)
{
Kill();
}
to this:
if(bIsAlive)
Kill();

I hate when someone does not use the braces ven if it means that its just one line. That is because:
1) The braces make it more readable and does not confuse me.
2) When maintaining such a code, if I also want to PunishTheKiller(), I have to either tag the two braces of the 'if' with enclosing code change comments or I have to comment the whole if block and rewrite it again. Something like this.
if(bIsAlive)
// Change # 1234
Kill();
// Change # 1234

------ OR ----------
// Change # 1234

----------------------------------
------- WHEN IT COULD BE ---------
----------------------------------
if(bIsAlive)
{
Kill();
PunishTheKiller(); // Change # 1234
}

Daanish Rumani on July 9, 2008 1:17 PM

Correction:

if(bIsAlive)
// BEGIN Change # 1234
{
// END Change # 1234
Kill();
// BEGIN Change # 1234
PunishTheKiller();
}
// END Change # 1234

------ OR ----------
// BEGIN Change # 1234
//if(bIsAlive)
// Kill();

if(bIsAlive)
{
Kill();
PunishTheKiller();
}
// END Change # 1234

----------------------------------
------- WHEN IT COULD BE
----------------------------------
if(bIsAlive)
{
Kill();
PunishTheKiller(); // Change # 1234
}

Daanish Rumani on July 9, 2008 1:19 PM

100 comments so far. Am I the only person here who suspects that this Spartan programming guide is tounge-in-cheek?

Charles Monk on July 9, 2008 1:42 PM

Most of the things look cool except that naming convention thing. In other words one should not be burn down variable names to letters or don't try to write obfuscated code while there are obfuscating tool already available.

Faheem on July 9, 2008 1:50 PM

this.issparta() ?

Mike Kingscott on July 10, 2008 2:51 AM

Yeah I definitely disagree about using shorter variable names. Though making them too long can also be a problem, like variableNameThatGoesForWayTooLong.

If you look out an individual line in their final coding sample, such as m.setContent(mp); you have to remember that m is the message and mp is the multi-part. Yeah I know its only a couple lines up but its still harder to read that then something like message.setContent(multiParts);

Also better named variables can help when it comes to compiling and debugging. For example, in the debugger when you watching variables its easier to know what message is then m.

Grom on July 10, 2008 2:58 AM

to yossi gil:

return x 0 ? x : -x;

This leads to returning -0, if 0 is the input - not so nice.
I ever use parenthesis when using the ?:-pattern for the question - i think it's easier to read this way.
If the code between the : is more complex, i use them even there ().

titrat on July 10, 2008 3:04 AM

This post of yours, Jeff, had by far the most impact on me from everything that I have read for the last two months. Willing just to put link to the original post, I eventually ended up writing a href=http://eterniel.blogspot.com/2008/07/spartan-programming-real-man-way-to-do.htmla full 3 page essay/a about Spartan Programming and the ways to write programs like a real man (I'm sorry I borrowed your 300 spartans screenshot).
It was... very entertaining. Thank you :)

Gary Schubert on July 10, 2008 3:38 AM

That C example is one UGLY piece of coding!

A person that uses if-else in that way should be taken out back and shot (or at least given one hell of a wedgie)

Sverrir on July 10, 2008 3:40 AM

Yo YG, the rzn ur wrng is vars 2 shrt is 2 hrd 2 read unless ur used 2 it. I call blsht.

Maybe you need to examine whether you are objectively considering that short 12 variable names are easier in general, or whether it's just easier for you personally. You must divorce your personal bias when making your recommendations because the recommendation needs be based on fact.

Code Complete provides reasoning backed up by studies (thks G). You need your own compelling, and scientifically valid data to contradict or at least call into question the results of the Gorla, Benander, and Benander research. Yes research is hard, but so what. Your assertion needs to be backed up by proof. Otherwise it's just opinion, and we've all got 'em.

Most people here, however, seem to disagree with you. So in this court of public opinion, you're wrong.

Not that you need to change, but just realize that what makes you happy isn't necessarily what makes anyone else happy. You have a hypothesis at this point, and nothing more. You've assembled and shared a list of personal preferences. Great, but don't pretend it's anything more than that!

Maybe rename the wiki to 'YG minimalist Coding Style for PHP which YG affectionately calls Spartan'

Also Note that I did notice in your final result you initially mention the Character Count and Token Counts as metrics, but you never tell us what the final result is, so what's the point? Seriously? These are pretty poor metrics to go by.

---

Is summation, I declare that calling something a programming 'style' belies the fact that it's just a collection of personal preferences. Code for yourself and code however you want. Otherwise code for someone else to read and maintain it.

RedGreen on July 10, 2008 3:44 AM

While I take full responsibility for giving html a try even though *no HTML* was clearly stated, feel free to write about non-rfc compatible and buggy auto-hotlinking in modern blogging platforms :)
Cheers :) And thanks again for the original post.

Gary Schubert on July 10, 2008 3:47 AM

Okay T.E.D. I'm sober enough to attempt a reply:

1) The first half of this works great... C has no such advanced programming constructs.

Yeah fair enough.

2) This works in C today, although it is only recently that compilers started allowing variables to be defined in loops.

Hmm.. pretty sure this has been in since at least ISO/IEC 9899 (adopted by ISO in 1999) but I don't have the standard handy to check.

3) No such thing as a private variable in C...

You can still minimise the accessibility of variables e.g. by using static variables.

4) No such thing as constants in C...

Hmm.. const.. page 39, KR (2nd edition)

5) ..I am sure it works just fine in C, whatever it is.

Yep.

6) ..it should work just fine in C.

Yep.

7) No standard componet library for C...

Fair comment - though it does also suggest off-the-shelf libraries, which, as you pointed out, are available.


1) No classes or inheritance support.

Yeah but the rule is Minimizing the use of conditionals by using specialized constructs - classes it just an example. It also suggests Ternarization which C has.

2) Works fine in C.

Yep

3) No classes, thus no action applicator classes.

Yep - can't really do this in C.

4) Works better in C than most other languages I've seen.

Yep.

5) Designed for C, obviously.

Yep.


So from my point of view, almost all of those rules work for C in some respect :)

Not that I actually agree with all those rules by the way, but I did want to defend C's honour... it's too old to defend itself :)

Graham Stewart on July 10, 2008 4:06 AM

Here is a meta-issue for everyone to consider. I tried reading most of the stuff written here, and noticed a couple of statements, which follow the following 3 parts syllogism:

(1) Programming practice X is good [bad], because it produces code of style Y.
(2) Style Y is good [ugly] code. For proof see part (3).
(3) I say style Y is good [bad].

The great fun in making such an argument is never shadowed by the futility of trying to convince anyone with it.

Is there an alternative? I am not sure. My attempt in making the case for Spartan programming was based on more or less agreed upon metrics, and then trying to optimize these.

The difficulty with my argument is that it is a universal truth that any one of these metrics, or any other metric for that matter, is that they can be used to ridicule the purpose they are set to serve.

There is a general agreement (I think) that in general, shorter code is better than longer code, all things being equal.

But, if you try to get the shortest possible program for any given task, you would quickly become a competitor of the obfuscated C code contest.

How does one quantify code quality? Is there a more or less objective method to asses quality?


Yossi GIl on July 10, 2008 4:26 AM

Yossi,

The trouble lies in proving that any particular style is good or bad.

One approach is to back your assertion with some data from a study. This is something that Steve McConnell does repeatedly in Code Complete and personally I find it quite convincing.

For example, on the tricky subject of a good variable name length (Chapter 11.1 in Code Complete 2) he first makes his assertion:

..Names that are too short don't convey enough meaning. The problem with names like x1 and x2 is that even if you can discover what x is, you won't know anything about the relationship between x1 and x2. Names that are too long are hard to type and can obscure the visual structure of the program..

...and then backs it by referencing an appropriate study...

Gorla, Benander, and Benander found that the effort required to debug a program was minimised when variables had names that averaged 10 to 16 characters (1990). Programs with names averaging 8 to 20 characters were almost as easy to debug.

Graham Stewart on July 10, 2008 4:52 AM

re C++ code and making the enumerated switched

This is a classic Make a desert and call it tranquility* neophyte simplification, the kind of code that looks really neat when on display as a snippet, but is a nightmare to maintain in the middle of a piece of real code

Why on earth is it better to break the ontologically significant order in the enumeration to save a bit of scanning the text? Sure you get the unexecuted code up the top, but by having an enumeration in place you get the same movement through every time, you have a geography to the code that is the same every time you encounter the enumeration. It also helps followers-up on the code-base to know what to expect, and to spot egregious errors

moreover - doesn't that mean that for ALL of the code moved forward the ENTIRE switch gets traversed EVERY time? Why is that better than a lot of breaks avoiding the traversing?

*ob ref Tacitus atque ubi solitudinem faciunt, pacem appellant

Ivodne Galatea on July 10, 2008 5:44 AM

I tend to agree with most of the ideas and for the most part have practised something close for years without giving it a special name. When it comes to variable names though, I disagree, I've gone the opposite way because I think it improves clarity, avoids errors and allows me to use my IDE as it was designed.

Andrew McCall on July 10, 2008 6:34 AM

I tend to agree with most of the ideas and for the most part have practised something close for years without giving it a special name. When it comes to variable names though, I disagree, I've gone the opposite way because I think it improves clarity, avoids errors and allows me to use my IDE as it was designed.

Andrew McCall on July 10, 2008 6:34 AM

Hi Jeff,
I read your blog almost everyday, and usually anything you post, has a bunch of links that give more information that what you state in your article.

This one was pretty disappointing, coz more than 50% of the content of this post, is from the source url. I am sorry for calling out on you, but as your reader, I have come to depend on your blog for daily insights into the work I do, and have hence come to expect more quality from you.

(I know my blog sucks, which is why I hard write and no one read it :) )

Shivanand on July 10, 2008 6:37 AM

Graham: I am of course familiar with statistical methods. I am not sure they can be used here: unlike many other fields of science, experiments are very difficult to conduct, and the interpretation is very subjective. As a reference point, consider application of statistical methods to determine whether a certain protocol is better than another in treating a particular disease. The design of such an experiment is not trivial, and there are companies which make fortune just by doing that. But, the problems in doing a software based experiment are much much worse - and any change to the fundamental variables will invalidate the results: change of programming language, programming environment, debugging task, debugger tool, programmer training, underlying code, etc. It would be nice if we could run experiments of this sort. I am not so sure this is possible - but I will check out the references you provide.

What I am looking for is a method, which obviously exists in other disciplines, of reaching some agreement, without the usual bickering.
Such methods must exist in fields such as law, humanities, and other, non exact, non empirical fields of science.

Yossi Gil on July 10, 2008 6:43 AM

titrat: I am not sure I fully understand the implications of returning -0. Do you say it may be less efficient? Or do you say it may lead to wrong results on some machines?

Yossi Gil on July 10, 2008 6:44 AM

2) This works in C today, although it is only recently that compilers started allowing variables to be defined in loops.

Hmm.. pretty sure this has been in since at least ISO/IEC 9899 (adopted by ISO in 1999) but I don't have the standard handy to check.

I suppose recently is a relative term. My first contact with C was in 1983. The reference book I still use for the language is copyright 1984. 1999 (and the subsequent adoption in compilers which would have taken a few years) seems quite recent to me.

3) No such thing as a private variable in C...

You can still minimise the accessibility of variables e.g. by using static variables.

Heh. Well, yeah. In most other languages this kind of thing (no program-global visibilty for objects not explicitly defined in an interface) comes out of the box. It didn't even occur to me that this would be considered a feature. But I suppose you could claim this for C if you really want.

It seemed to me this technique was talking about creating what we used to call opaque types (private members for you whippersnappers). C++ can do that with private and protected. With C, no dice. The best you can do is declare the thing a void * and redefine it properly in the .c file (Security through obsurity). That's generally a lot more work than its worth.

T.E.D. on July 10, 2008 7:35 AM

The final refactored sendEmail is a much cleaner example of coding, but are Spartan techniques really the cause of this? Breaking the routine into smaller functions isn't some new method to achieve readable code. My problem with the code: !OO. Make the email message an object with appropriate properties and a Send method! Oh wait, what language am I thinking in?

Paul Schirf on July 10, 2008 7:50 AM

name. When it comes to variable names though, I disagree, I've gone
the opposite way because I think it improves clarity, avoids errors
and allows me to use my IDE as it was designed.

I sent the link to the rest of my group here, and that was the most contraversial part for us as well.

Its possible that part of the problem is just confusion on our part. There's no explanation for short names technique, and the link is broken. Someone more of an expert in wikis than I hacked the address manually and got a look at the page, but I'm holding out for it to get fixed (hint. hint).

What we came away with is that if it is a reaction to names like Electronic_Warfare_Tactile_Subsystem.Cockpit_Radio_Aids_Tacan_Package.Tacan_Output_Bytes_Written, then we'd agree. If instead it is an affirmation of names like fgwios (the gross weight of the aircraft, as a float, formatted for the Instructor Operator Station), I'm afraid we part ways here.

T.E.D. on July 10, 2008 8:03 AM

To Paul Schirf: I am glad you liked the final version (again, I am not sure how we should go about quantifying liking...).

The point here, which I should probably have stressed better is the following. Terse names, that is one or two letter names, make more sense if the context is small. Consider for example identifying an individual person. Within the small family cycle, a one syllable nickname would do. In a classroom, you may need to use the first name spelled in full: Paul. When the individual is part of a larger audience, say, participants in a forum, you may need to write Paul Schirf, and if you need address this individual among all citizens of a large state, you may need to write: PaulSchirfMaleOfPalmSprings.

The point is the following: the smaller the scope, the shorter the name. Conversely, if you strive for short variable names, you will be gravitating towards smaller modules.

There is another point to make here (eventually I will add this to the wiki): in the course of decomposition, you often come up with small, yet general modules. These modules often make terse names even more necessary.

In the Paul Schirf example, suppose you have a module with code designed to serve as back door of an application, e.g., by testing if (user==Paul Schirf) { do lots and lots of special things }.

Now, in decomposing the body of the conditional, one may come up with a small sub module designed to send a birthday greeting by email to the individual. I would expect a signature of the sort of,
congratulate(Person p)
to that module. Conversely, if the same code was inlined into the main module, you would more inclined in using a verbose name for the user to congratulate, by writing e.g.,

User backDoorUser = ...;
Message congratulation = ....;
send(congratulation, backDoorUser)

The point here is that the strive towards general, non descriptive names, is stimulates and is satisfied by good modular decomposition.

PS.
Others here may suggest using congratulate(Person person) as signature for the submodule, i.e., naming the parameter person rather than p. I am interested in understanding better what makes people prefer the more verbose version in such a case.

Yossi

Yossi GIl on July 10, 2008 9:06 AM

T.E.D. : Would you like to help in revising the C example? Would you mind if I used your suggestions?

I'd probably like to help in revising all the code in the world, but I don't think I have that kind of time. :-)

As for using my suggestions, that's what they were made for.

The way I see it, we are all in the brotherhood of software developers here (female readers included). It is our moral obligation to teach and learn where we can, and to try to leave behind code for our fellow brothers that come behind us that won't cause them unnessesary grief and time at work away from their loved ones. Our hope and prayer is that those who write the code we read will do the same. So rest assured that I'll be using any ideas I get from you all without shame.

T.E.D. on July 10, 2008 9:17 AM

Interesting that people here mention C++. I first used the term Spratan Programming in the context of C++ - and even gave a tutorial on the method in the TOOLS USA 1996 conference.

(believe it or not, there is an acronym behind the term)

The Wiki page was written to help my amazing students train with the method.

Yossi GIl on July 10, 2008 9:22 AM

While the smaller code sets create more understandable small variable names, they only do so because our brains can maintain contextual understanding across a few lines. That doesn't mean the small variable is more understandable; it simply means it can be understood.

private static MimeBodyPart makeBodyPart(final File f) throws MessagingException {
final MimeBodyPart $ = new MimeBodyPart();
final FileDataSource fds = new FileDataSource(f);
$.setDataHandler(new DataHandler(fds));
$.setFileName(fds.getName());
return $;
}

Is this really more readable than?

private static MimeBodyPart makeBodyPart(final File file) throws MessagingException {
final FileDataSource filedatasource = new FileDataSource(file);
final MimeBodyPart result = new MimeBodyPart();
result.setDataHandler(new DataHandler(filedatasource));
result.setFileName(filedatasource.getName());
return result;
}

Paul Schirf on July 10, 2008 11:55 AM

Rhywun, tk., and Graham Steward: Using a return object to enforce
single return is cargo cult programming. You're accomplishing nothing
and you're doing it because of an old misunderstanding of Dijktra's
and Hoare's Structured Programming. One principle was one entry
point, one exit point but as soon as you have a changing return
object, you've already violated that principle (for all intents and
purposes) and you've reduced clarity.

There's nothing goto about early returns, quite the contrary.
(Unless you count lambda as the ultimate goto.)

Swedish Code-monkey: What is readability? I see this word, I hear
this word but there's never a definition. Spartan programming is
clearly defined with a number of metrics and practices, unlike
readable code which in my experience usually turns out to be a word
for redundant, overly explicit code.

Danish Rumani and j450n : Blocks are for more than one statement; i.e. when
you're using side-effects or state. If you're adding additional
side-effects to a conditional clause you better well be on the look
out for bugs, since that's something that should be done carefully, if
at all. To my eyes, every {}-pair means Here be side-effects!
similar to the exclamation marks in Scheme, and I use them
as sparingly. In your PunishTheKiller example, you've added
state-changing functionality.

Sunnan on July 10, 2008 11:59 AM

Well, maybe it is...

I still prefer the idea of making this an object. In doing so, the result is far more readable.

Daniel hit the nail on the head when he said EXPLICIT getters and setters are noise, not signal. The language should support object.var = expression;

I also personally dislike case sensitive languages. Context in natural language rarely changes based on case. Oh sure, there are exceptions... and they obscure meaning.

Paul Schirf on July 10, 2008 12:05 PM

Paul Shirf: Yes.

Sunnan on July 10, 2008 12:09 PM

What is readability?

I've been in code reviews were non-coders were included to check for code readability. The idea is that, if the context of what is taking place in the code is clear to a non-geek, then its code that can be debugged easier in the future by coders who have incomplete understanding of the code outside of the routine they're looking at. And don't say that this isn't one of the benefits of OO! Encapsulate context/meaning!

Paul Schirf on July 10, 2008 12:12 PM

This looks pretty darn close to what McConnell advocates for in Code Complete, doesn't it? If not explicitly (although using enumerated types and case statements this way *is* explicit) at least philosophically.

Mark Kohalmy on July 10, 2008 12:48 PM

lesscode.org

gonna leave it like that, but http://lesscode.org/about/ says it succinctly: use less code to get more done. It focuses more on the solution as a whole, rather than individual techniques in various platforms. Sometimes the bigger win is not to refactor a function or a file altogether, but the platform altogether.

angch on July 10, 2008 12:51 PM

@Bill
And I will use it(Williamprogramming) to write bad code.
Then what?

Niyaz PK on July 10, 2008 1:22 PM

Now the time has come for me to conclude:
(1) It depends.
(2) Its all about trade offs.

Niyaz PK on July 10, 2008 1:25 PM

I usually apply the DRY principle on my code.

Although it is not actually the same as spartan programming (how much I like this name !), I think it shares the basic concept.

Stefano on July 10, 2008 1:31 PM

Not to kiss-up to my professor, here's another way to examine terse variable names.

First, variable names are actually used for two purposes:
1. Specifying a name to access the area in memory (which isn't mandatory in C++ signatures, but is in Java interfaces)
2. Documenting the meaning of that variable

Just as you should prefer writing code that explains itself without comments, so should you strive for short and well-written methods that describe the meaning of the variables they use. An extreme case of this is Java's System.out.println(), where an entire group of methods have the same meaningless argument name x.

Looking at this the other way around, terse variables are a kind of litmus test for method complexity: if you have many long- and descriptively-named variables (which are a mental-load on the person reading the code), it might mean that you should break-down the method.

And as for shrt vars tht r hrd 2 read, @RedGreen, according to this style, variable names are either one-letter or the full word (plus s for collections). Never shortened names like you suggest, not even msg; either message or m.

uv on July 11, 2008 3:23 AM

The Spartan programming methodology *dictates* minimization of horizontal complexity [...]. This is because *it is believed* that horizontal scrolling is even more intellectually demanding then vertical scrolling. Further, *it is believed* that deep nested control is difficult to understand.

This guy is dictating to me based on a set of beliefs? Where's the evidence? Sounds like religion to me.

Thwock on July 11, 2008 4:06 AM

@Sunnan: Rhywun, tk., and Graham Steward: Using a return object to enforce single return is cargo cult programming. You're accomplishing nothing and you're doing it because of an old misunderstanding of Dijktra's and Hoare's Structured Programming...

No cargo cult here. I can't speak for the others but I'm doing it for two reasons (neither of which have anything to do with Dijkstra):

1) it is the preferred approach in my company's coding standard. The standard contains wisdom from 20 years of writing code and sticking to it keeps our code consistent.

2) I'm also a member of the code review team and I know from my own experience that I find it easier to mentally parse and trace possible code paths in routines with a single return point.

Also, as I said before, I do sometimes use early-returns for establishing pre-conditions at the start of a routine (e.g. if param1 is null then return -1) or in very short routines (up to about 10-15 lines) where readability won't be affected. But generally I prefer to keep them to a minimum.

For the record, Code Complete 2 (Chapter 17.1, Unusual Control Structures) has the following advice, which I believe fits well with my approach:

- Use a return when it enhances readability.
- Use guard clauses (early returns or exits) to simplify complex error processing.
- Minimize the number of returns in each routine.

Graham Stewart on July 11, 2008 4:32 AM

Personally my only reasoning for using single return points is when some memory is allocated or some library/structure/function is initialised that must be shutdown before exiting the function (e.g. fopen/fclose, CreateWindow/DestroyWindow etc...).

Here I also use that most heinous of all evils the goto statement. Its pretty much the one place I will use it...

In other situations I don't find it helps... it just makes an extra step when reading code, which may or may not involve using Find or a lot of scrolling about in a file in order to track what I think the code should be doing... I like to avoid these sorts of constructs, as much as they have little or no effect on the compiler or program output.

Jheriko on July 11, 2008 4:47 AM

@Jheriko:

So (based on your posts):

you leave member variables as public to save writing accessors; you don't use foreach or other language constructs that hide code; you avoid using existing frameworks; you write your own versions of basic library routines like strcat(); and you use gotos.

Man, I'm glad I don't have to review your code! :)

Graham Stewart on July 11, 2008 5:11 AM

Rather than Spartan Programming, I'd call it Laconic Programming. I know for a fact that the Spartans used COBOL.

Paul Schirf on July 11, 2008 7:43 AM

«Back | More comments»

The comments to this entry are closed.