Saturday, December 17, 2016

Ten programming hints. For Christmas (part2/3)

Let's get on with the programming-hints-list, shall we?

5. Readable code, // Commenting

What I'm about to say, is probably NOT what you learned at school. School often tells to throw a whole book of comments in your code. So somebody else can pick it up more easily… If he reads that whole book, that is. Hence, there is often more comment than actual code. And who's reading that? Exactly. Nobody. You're not reading it, because you already know what that code does. That other guy isn't reading it either, because nobody likes to read a whole book. Especially not if its code-comment. If code cannot be grasped within a short amount of time, programmers tend to call it junk and start to rewrite it. With their own junk.

I'm not saying comments are stupid. I'm saying just don't overdo them. In the basis, your variable and function names should be self-explanatory enough. Captain obvious, but still we tend to complicate those functions by letting them do additional stuff the reader wouldn't know. If we call:
                BarberCutMyHair( Prince_Of_Bel_Air_Style )

We don't expect it to paint my toe nails as well, get outa here. Keep it simple. A "Cosine" function performs the cosine. Nothing more, nothing less. And if you're getting a big load of functions, split them into groups or modules. And eventually make helper-functions on top that do multiple things (and clearly explain that - here you have your comment). Using the right names is worth far more than some comments to explain afterwards.

       // name             : addTwoNumbers
       // params           : A, B (int) the numbers to add
       // Returns          : The sum of A and B (int) 
       // Description      : Add A to B 
       function addTwoNumbers( const A, B : integer ) : integer;
       var C : integer;
             // Add A to B
             C := A + B;  // Store in C
             // Return C as a result
             result := C; // C = result
             // End of function

And now read this.

       function addTwoNumbers( const A, B : integer ) : integer;
             result := A + B;
Your teacher may not be happy with the second version, but we are smart enough to understand what's going on right? Get rid of useless comments, and keep them for grouping your function into sections, or for REAL important notes. So... if you see comments, you are actually TRIGGERED to read them, because you know something useful is in there.

Speaking of Readable code. I hate those auto-completion editors that force the tabs for you (some do a good job though). What I always do, is adding one or few tabs on the left side. You know why? So I can forward TEST / TODO / ERROR comments and code to the far left. If I scroll down quickly though a document, I can immediately pick out stuff like this, without even reading the code. Thanks to their ugliness, comment lines like these stand out, and don't get lost between the others:

       function addTwoNumbers( const A, B : integer ) : integer;
             result := A + B;
// TEST. Lets multiply instead baby
result := A * B;

This is the type of screen I'm looking at 99% of my life, so I'd better make it tidy.

6. Size matters. Small sizes.

Starting a new project, excited. Code rambles out of your fingers like a MG42 machine-gun. Man, you’re just too cool. Four months later, it’s done, and you move on to another project. Four years later, some annoying bitch in the office emails that the Export-to-Excel-button doesn’t work anymore. It says “Access Violation F00236A”, or something. Lame.

If your program were to be 400 lines only, I bet you’ll patch it up in no time, doc. If it’s a 40.000 line program doing a lot more, thing may get a bit more tricky. Maybe there are 10 screens with “Export” buttons, but of course she forgot to mention which one, or what options she checked. And if there are 4 files, 6 different classes, and a few thousand lines behind those buttons, hidden deep inside the code, I’m pretty sure you’re not that excited to debug it, four years after.

Well, things like that happen regardless. And all the lessons you learn here about avoiding stupid comments, using “Try Catch” and error messages, Modular design, or having Readable code in general will be helpful. Hence, you may even want to consider your work tools at forehand when this scenario is to be expected. C++ (or Delphi / Java / VB / … any written language) are relative hard to analyse and fix, in general. PLC ladder diagrams or other tools that let you code in a more visual, “block-to-block”, way, are usually much easier to figure out. Eventually even by another person, years after.

It’s one of the reasons we use IQAN at work for mobile agricultural equipment. I do get phone-calls –and always on Sundays, when I’m shitting, drunk, or hanging upside down- with strange problems on eight-year old machines. The track-suspension doesn’t work, why?! Obviously I don’t remember all the conditions and possible error situations, so I’ll have to dig in the code. And thanks to the straight-to-the-point, visual way and live-debugging tools, I only need a few minutes usually to get familiar with the code again and figure out what’s going on.

What I’m trying to say here is, the choice of this tool saved us dozens of hours, money, and angry clients waiting on a broken machine. As much as I love “normal” coding, I would never pick C++ or the likes for this type of application.

But, if you are stuck to a written language, then at least try to keep your code clean and compact. Less lines = less reading = shorter learning curve = quicker results. Nice and all, but it’s not that Bill Gates can order his programmers to downsize the Windows OS to 600 lines, or a 64K program. No, of course not. But still, you could:
·         Split one SuperProgram up into smaller task-specific “Apps”
·         Make reusable modules (see 7) and build these “Apps” on top
·         Don’t bang all your code into one huge file
·         Move the bigger / more complicated code into the background (files, modules, …)
·         Where possible, use easy-to-make (configuration) files or visual ways to define behaviour or data, instead of hard-written code. Easier to tinger afterwards.

In other words, if you’re looking for “Export” related code in your program, don’t slap the reader with other functions, classes, files or global variables that have nothing to do with it. And if possible, keep the programmer completely out of certain sections. If my graphics look like crap, I'm not digging in actual OpenGL library code either.
On a more global level, it’s useful to draw boundaries. And be clear about them. Why did the office lady get that “Access Violation” in all of a sudden? After four years? Something must have changed apparently. Newer version of Excel? Bloody Windows10 again? Tried something different than usual? And there you have it: her computer crashed because she spilled donuts in the CD-ROM drive, so IT gave her a new station and OpenOffice was installed instead of MS Office. Yep, that won’t work. Stupid lady. Stupid IT. Or stupid program, for not telling what is missing? Don't promise rockets if you're making a boat.

7. Reusable code & Modular design

When making a bigger, multifaceted application(set), chances are you’ll get some common functionality beneath it. For example, let’s say you work a lot with XML files. Instead of re-writing a Reader/Writer function in every program, it’s better to make a “XML library” that can be reused in all your programs. A library could be just some shared file, or a DLL, statically linked library, or maybe even an API running on some web-service. Depends a bit on the circumstances.

Anyhow, as you also learn with OOP (Object Oriented Programming), you’ll be making reusable code-parts sooner or later. Classes or functions that are used on multiple places, and inherited by “superClasses”. After a while, you will also learn that some of those classes can also be useful for other programs. So you probably share code-files to begin with. When growing even further, you will get a whole collection of such “common functions”. Could be a XML library, a math library, sound, graphics, communication… So, you decide to pack them together in a DLL, API, Framework, or whatever you’d like to call it.

At least, that’s what you should be doing. However, as logical as it sounds, it can still be hard to make truly reusable functions, worth being in their own library. When you try to rip out your “AwesomeStuff class” and put it in a separate library (project), chances are it won’t work. Because “AwesomeStuff class” is depending on a lot of other code or variables in your original program. So you start moving them over as well, even though you feel those functions don’t really belong in a separate library. Setting up the same stuff on two places, too specific, too much ties and principles inherited from its original host program.

That doesn’t make good modular code. I’ve seen people trying to create their own “AwesomeStuff DLL” just because they feel it’s a good thing to do. But when looking into that DLL, it’s not all that awesome. It’s actually pretty much bound to a very specific application, and for the sake of easy debugging, he or she would have been better off leaving that code just there –in its original host program. It’s like stealing somebodies child, putting it in another house, and then tell its original parents to raise it.

Still, you have that urgent feeling you should be moving your more common functions to a library; modular design, you know. Cool, but do it in the right order. When restarting Engine22, or when making a machine-display Framework at work, I started building these modules first, instead of making the final program and ripping out common functions. Or actually, I started on paper, making up what would be good candidates to become (stand-alone) modules. There are no strict rules, but at least some guiders you can use:

·         Group and separate logically:
o   As mentioned in Point #5, the name of a module should be self-explanatory as well.
o   “Sound” does Sound. Not Physics or Aerobics.
o   Then again check if it’s worth making a module. If there is just a few functions, giving its own module might be a bit overdone. Don’t end up with fifty DLL’s for every fart.
o   Draw on paper. Robust names, not too much dependencies? Can you explain this to a non-programmer? Bingo.

·         Try to disconnect your head from one specific Target application (“Tower22”):
o   Use functions that can be used in multiple scenario’s
o   For my Machine Display Framework, I thought about harvesters, but also about cranes, dumptrucks, ships, …
o   But do not implement scenario-specific features. Provide help-functions they can use instead. Keep the actual Rocket-booster logic for “Spaceshuttle.exe”. Or make layer in between for that.
o   So keep it generic, yet clear.
o   A function is what it is. Nothing more, nothing less. No hidden bonus-goodies.
o   Develop and compile your library as much stand-alone as possible
o   Use (small) test-programs to, well, test it

·         Place yourself in another developer, using your library
o   The main purpose is to SIMPLIFY; do hard work via a few easy functions
o   Again, good naming is key
o   The less your library exports, the easier to learn
o   10 functions are easier to master than 1000 functions
o   But again, be careful not to make your functions to much “multi-purpose”. A “readXMLfile” should just read the file. Not actually trying to parse it and do stuff. Split up when needed.
o   For every function, ask yourself if and how the end-user will use it
o   Even better, let another programmer actually try your library. Filter out the confusions, bugs, the dos, the don’ts.

In a nutshell, try to separate common & application-specific parts. A library like OpenGL offers a big bunch of generic 3D-drawing functions, but doesn’t do any specific rendering-technique for you. It does not calculate collisions, draw shadows or make awesome reflections. It just gives you the tools to get there easier.

It’s not uncommon to make a more specific module on top of more abstract modules though. The Engine22 “Graphics” module is basically a wrapper that brings all the OpenGL magic together. So that I don’t have to worry about it anywhere else in the code. Not a single OpenGL command is written outside that module.

No comments:

Post a Comment