Tuesday, November 8, 2011

Writing Code that Doesn't Suck


Striving to write good code should be the goal of every Software Developer. Writing code that is easily maintainable, robust, simple, and makes sense is no easy task. It takes years of practice and uncountable hours of time to become a good Programmer, and the job is never really done.


Recently I ran across these few lines of code that break many of the following tips I’m going to give you. I’ve seen some code that the Developer who wrote it clearly didn’t understand. It contained no error handling cases and only worked in the success case (1 out of 50 paths). In every other path it would fail!


For example, in one situation, 10 variables are created (none are initialized, another problem) with names of x,x1,x2,x3,x4,x5... and so on. The Developer then proceeded to use these 10 variables in the next 500 lines of code. When I asked the Developer what these variables actually were, he couldn’t even explain it to me!

With the above in mind, I thought of a few tips that would really help a few people write much better code. I realize that a good amount of the following tips are CS101 type comments, but without even following these, you have no hope of writing any sort of manageable code.
How to combat:

  1. Communication
    1. It’s far better to ask someone for help if you don’t understand how something should be done than to write some spaghetti code that may solve the problem for the Success now, but may become a minefield for every other case later on.
  2. Following some basic steps when writing code
    1. Null checks - Don’t create mind field code that crashes on specific cases that you haven’t checked for
    2. Simplicity
      1. Don't write 4 lines of code when 2 will do. If a block of code requires more lines of comments to explain what it does than the code itself, you need to rethink how it’s being done
    3. Comment frequently
      1. Having the problem of too many comments is always a better situation to be in than having no comments at all. I would rather see a story describing some method/function than no comments at all.
    4. Name variables that make sense
      1. Names like X and B are simply no help, and they show that you were simply too lazy to write a more descriptive name of the variable. This will quickly come back to haunt you when the code gets even slightly more complicated. Take the time to make informative variable names! You will thank yourself later.
  3. Factorization
    1. Avoid Copy/Paste of Code! If you need to use a block of code over, it’s time to refactor that code into a function/method
    2. Anytime you can simplify code that has been written, do so!
    3. Reuse any code that is written as much as possible
    4. Always try to improve the code you have written.

All right. I’ll get off my soap box now. I hope these tips help you become a better programmer. Feel free to let me know if you have some tips of your own.  

       

7 comments:

  1. In my view code written that way does suck.
    I dont like comments or null checks

    ReplyDelete
  2. Anonymous you are right. 2.1 and 2.3 both "suck".
    2.3: Should be like 2.4 for methods.
    "if (object!=null)" everywhere in the code deflect the important things...avoid this!

    ReplyDelete
  3. I generally find the best way to avoid null exception issues is to ensure I always return an initialized object such that I can call a property on it and while that property might be null, the call itself won't throw the exception.

    ReplyDelete
  4. null checks is a big problem because it makes the code unreadable when taken to an extreme.

    best way to avoid them as anonymous (not me :-)) said is write functions that don't ever return null, like that we don't have to add null checks everywhere when we call those functions.

    of course if third party libraries return null, then we cannot avoid it but that can be isolated.

    instead of null we can return empty collections, or "Special Case" objects, this is not always practical to do though.

    There are actually languages out there (I think Kotlin/Ceylon) that aim at among other things solve the null problem like this: if the code compiles, then we are guaranteed to have NO null pointer exceptions.

    For comments, I prefer good names for functions and variables than comments to be honest.

    Most comments in code are basically useless if we name everything properly.

    They should only be used to explain WHY something is done in a certain way, not HOW.

    ReplyDelete
  5. I to try to avoid null checks by trying to not return nulls in method I write. Maybe what I return is an empty object - usually empty collections are a good idea and supported by Collections. Collections are usually iterated over or sorted in someway so an empty collection usually results in little to no harm.

    But sometimes a null return value has some valid meaning, and unless the contract for the method is that it won't ever return a null, then defensive programming more or less requires checking for that null.

    Worse than if (something != null) is controlling logic flow with exceptions. I would rather have some method that retrieves an object from a data store return a null than have it throw an exception because it didn't find anything. Then the caller uses the exception like a null check, only way uglier. I see a lot of this.

    Copy/paste code is bad, I have seen coders copy/paste my class, change less than ten lines and call it a new class, instead of maybe extending it, or adding a new method.

    But worse is doing the same thing in a different way - i.e., reinventing the wheel. Now, not only do I have to fix the same bug, or add/change functionality in several different places, I have to understand the multiple ways the same functionality is implemented and the chance that I will create yet another bug is much higher that with simple cut/paste (the latter I can usually replace with one block of code called my all previous places that code was).

    I am much less likely to want to refactor different implementations of the same functionality, especially if it is complex, because *maybe* there was some little difference in behavior I will miss, or break, or introduce.

    I could write for hours on the horrendous code and bad practices (in coding) I have seen over the last few years - you would be amazed at what I have seen; things you would think a first year intern would know not to do. But someday I will write my own blog about what I have observed.

    ReplyDelete
  6. "I am much less likely to want to refactor different implementations of the same functionality, especially if it is complex, because *maybe* there was some little difference in behavior I will miss, or break, or introduce."

    That the limit of code reuse. You can really reuse code too much. You see this when you code become too generic that you don't understand what it really do anymore, where you need 10 factory to do the job and the implementation have many many if conditions to take care of all possible cases.

    This kind of code become unusable, unmaintenable and unrefactorable. It can be better to have 2 or 3 version of a code so duplicated and in different class/packages projects.

    The only code that is trully reusable is API code. That's what you have in the JAVA or scala API for example.

    But it is really difficult to make thoses. You need easily 10X the time to ensure you API is of good quality. It require to be an expert so you anticipate all cases. And more every few year you need to write a new API to replace it.

    Think of Collection API in JAVA. It is good. But it is really different now than it was before generics. And it will be really different in JAVA 8 with map/reduce and fonctional programming than it is now.

    Scala changed rewrote it collection API already...

    Trully generic and reusable code is increadibly powerfull but also very difficult to write and costly. You may not want to abuse it.

    ReplyDelete
  7. I disagree. I have had pretty good luck with reuse of code - but as you said, not so complex that it is hard to understand or maintain.

    Yes, I have seen code that was so abstract it was hard to understand (for me - the author had no problem with it, but anyone besides the author did).

    It pretty much comes down to the judgement of the developer - he/she has to understand at what point some code module is too complex in any respect.

    But when I am talking about copy/paste or reinventing the wheel, I am talking about modules where it should be obvious to all but the inept and incompetent who create these module, that the code is ripe for refactoring into a single module. I see this kind of code every day - it depends more on the level of competence of the developer than on judgment of the developer - the inept/incompetent developers who write this code are the kind that create methods in the range of hundreds of lines to thousands of lines long just to give you an idea what I am talking about.

    In my opinion there is a huge spectrum of code quality out there; damn good, pretty good, good, okay, mediocre, poor and OMG who thought up this ******* mess?

    Until you have seen the "OMG who thought up this ******* mess?" kind of code you have no idea just how much suckage there can be in code. Things like refactoring and reuse don't even come into play - the old axioms about draining swamps while having to fend off alligators comes to mind. Sometimes the best you can do is to wade through it without making it any worse - and even then sometimes you do.

    I prefer to make things better, even if it just means breaking out huge chunks of code into methods (which themselves are somethings 100+ lines long). I really like when I can replace 100 lines with one call to a Java JDK API (which happens all too frequently).

    On the down side, such refactoring can make you vulnerable because the original authors of the code rarely appreciate it, and if something breaks then you become a target. In such environments the code often gets worse over time because the bloat increases, people who want to refactor are discouraged from doing it, and others are afraid to.

    As for the JDK API - I haven't looked at all at Java 8. In my experience just because a feature is there doesn't mean I have to use it, and I am often a couple of years behind the bleeding edge of the API, if for no other reason than most deployment environments are too (some targets are still running Java 1.42, many are still running Java 5 even though it was end of life over 2 years ago, and a few have gone to Java 6) so there is little reason to be writing to a VM version that most of my customer/clients won't be using.

    That said, on the other end of the scale, I have seen a lot of code that uses really old tech (ten years old) and has almost nothing in the way of good practices that have been around for years (enums, generics, and so on) that makes code safer and easier to understand - let alone comments.

    I could go on and on, but I think I want to do my own blog on this - the practices mentioned in this blog pale by comparison to the "OMG who thought up this ******* mess?" code I deal with daily.

    ReplyDelete