Programming Etiquette/Convention/Methodology

I have often come to realize that to become better at something you first have to know that something exists. As an example before I started working with a microprocessor I had no idea what it could do. I heard of running LEDs with it, but it hardly seemed that powerful. After having a class directly working with the Tiva Launchpad and us implementing our own libraries for a daughter board I was quite surprised by the power of a microprocessor. Once I knew what the thing could do it greatly expanded a number of projects I thought I could work on.

Now, this class and a couple of others gave me a decent background in working in different programming approaches. However, I came across a variety of programming methods, etiquette, and conventions. All of this information seems passed on through example rather than being told in some tutorial. I have tried tutorials, but several only ever focus on technical details rather than simply having good programming practices.

Therefore, I was interested in asking what good programming practices you think are worth taking note of. I can list a few examples of what I have learned.

-Code readability: I believe you should be able to give someone your project files to someone and they can completely understand the project without saying a word to them. This would include things from your programming structure (all functions in a single file or across many), descriptive variable/function names, commenting on arguments and purpose, commenting on algorithm heavy sections, etc.

Sticking to a particular style. For example, I have done away with tab indenting and stick with manual 3 or 4 spaces per nested portions. Or I use //**** (80 characters) ***** to highlight the differences between all functions to more quickly navigate through my own code. Coding within 80 characters.

-Design Structure: The method I find I like the most I have come across is the idea to divide your project into several pieces to more easily navigate and plan the project. Usually, the more the better. For example, recently I implemented an alarm clock. My setup() called a boardInit() function (which had its own short library) which called 4 other initialization functions for push buttons, 7-segment displays, and a piezo buzzer. These each had their own designed libraries. In which case, the loop() function did only three things. It looked to enter three different states of "set the alarm", "count down", and "entering an alarm state".

-Removing Errors: The idea to make sure you scan for as many errors in a function as possible. If a function has 3 arguments each argument should be scanned for potential error (if possible) and the function should return something to indicate success or failure of the function. Each function call should be scanned for error as well. This greatly helps in troubleshooting. However, implementing becomes tedious.

Is there anything else you have come across which is good programming practice?

What you call "removing errors" is more commonly known as validation. Yes, it is a PITA sometimes. You have to not only detect, but handle them somehow (otherwise it's usually pointless - just an exercise in minimizing harm). But the exception mechanism in many languages was designed to make that easier.

I can think of another principle - encapsulation. It's an important part of OO. But you don't have to have full blown OO to use it. In C you encapsulate many things because of scoping.

Here's another one that coders don't think about nearly enough - re-useability. It's great to write a clever sort or something. But now imagine that it is a generic component that you could almost just plug it into subsequent programs. Your productivity just multiplied. Save 'em, collect 'em... trade 'em with your friends!

There are a lot of "standards" for programming, and a lot of them are obsolete or irrelevant.

The way you organise software is going to be quite different for a microcontroller, compared to a large system which might have hundreds of people working on it.

A lot of the common strategies for handing exceptions simply don't make sense on a microcontroller which has no operating system nor error reporting capabilities.

Limiting code to 80 characters is a throwback to punched-card Fortran in the 1960's.

There is nothing wrong with writing clear and well-commented code, with descriptive variable and function names.

I think the 80 character limit restriction is rather useful even now still for scanning through a file visually for something. If I have an array of say 200 values I code myself (say for say notes/duration), then splitting it into multiple lines of 40 characters is easier to sift through compared to having it all on a singular line.

80 seems a good number too on most screen resolutions to see the whole line at once without having to readjust my eyes because the line goes out of my cone of focus.

For the validation I was thinking of some more simplistic things. Even if you program a function yourself that will never take I/O from another person it good to set checks on stuff for your own sanity on more complex projects.

if ( number > 50) {
   Serial.println("Number greater than 50.");
   return -1;
}

I have lost track of how many times I was developing a test for testing one part of my project only to have something like the above fail and I did not have this catch in place and wasted time figuring out where I messed up. When by adding this little bit of information coudl have saved me much time.

Oh, and on the re-usability I forgot about that. Rather than using constant values give them macros.

if( number > UPPER_LIMIT ) is better than using 50.

I find using scaffolding code makes debugging easier. For example, using xeylode's example:

#define DEBUG     // put at very top of source file

// ... a bunch of code...

#ifdef DEBUG
if ( number > 50) {
   Serial.println("Number greater than 50.");
   return -1;
}
#endif

When the code is stable, just comment out the first line and all the debug statements are no longer compiled into the code but can be left in the code should they be needed later.

Another thing is do is always have a default case in a switch:

switch (val) {
   case 0:
      // ...whatever
      break;
   case 1:
      // a bunch more case statement blocks...
   default:
      Serial.print("I shouldn't be here, but val = ");
      Serial.print(val);
      break;
}

Also, you're probably going to find that, if you program professionally, there will likely be coding standards in place and you may not have a choice on some things.

xeylode:
-Code readability: I believe you should be able to give someone your project files to someone and they can completely understand the project without saying a word to them.

That is a nice goal but it may be difficult to achieve because so much depends on the knowledge and enthusiasm of the reader.

My goal (which I mostly fail) is to write code that I can understand 6 months later with a single read through. I like to break the code into many small self-contained single-purpose functions with meanignful names so that reading through loop() immediately gives a good overview of what is going on. EG

void loop() {
  currentMillis = millis();
  readButton();
  updateOnBoardLedState();
  updateLed_A_State();
  updateLed_B_State();
  switchLeds();
  servoSweep();
}

taken from several things at a time.

Using meaningful names for functions and variables obviates the need for many comments.

Using self-contained single-purpose functions means they can be developed and tested separately and then treated as a black box. They are also easy to copy into other programs.

Being able to develop and test a small piece of code can be very important when working with unfamiliar external apparatus.

...R

Scaffolding works best when it doesn't cause the rest of the structure to topple unexpectedly :wink:

   Serial.println(F("Number greater than 50."));

I have done away with tab indenting and stick with manual 3 or 4 spaces per nested portions.

Excellent. Tab indenting drives me crazy. I convert them to spaces at the first chance I get. Not because tabs are bad, per se, but because everyone (including web pages) decide on different tab stops.

[quote author=Nick Gammon date=1429521180 link=msg=2195309]
Tab indenting drives me crazy. I convert them to spaces at the first chance I get.[/quote]
I do the opposite because I like to have white space visible in Geany AND because Python is very picky about whitespace and it is easier to see that it is correct when I use tabs.

I also find it easier to deal with badly formatted code examples if I convert spaces to tabs.

Variety is the spice of life.

...R

I like tabs exactly because they are so flexible. You may want to indent 3 spaces, while someone else may want to indent 5 spaces. With tabs you can both have your way. Indenting with spaces "hardcodes" the indentation to some number of spaces.

Of course, it should go without saying that tabs should not be used for alignment; use spaces for that. Therefore, my personal rule is "indent with tabs and align with spaces".