Saturday, January 30, 2010

Things I Wish I Knew...


When I was a young child an adult friend of mine took me over to his house and showed me his Commodore 64 computer. From the moment I saw the music software and the other things that he could do on his computer, I was hooked.

From that moment on, I played with computers whenever the opportunity arose. Before long, an uncle of mine, who was a computer programmer, sent me a Radio Shack TRS-80 computer. My interested piqued, I started programming in BASIC, going to the library to get programming books, and saving my money for computer parts. My interest for computers never died.

Years Later

Years later I found myself studying computer science (CS) at Eastern Washington University. I enjoyed it. Aside from some occasional frustration at my inability to easily do a homework assignment, I loved it!

Then, a bit of realization struck, which I didn't fully understand at the time:

  • I never heard version control systems mentioned
  • The instructor's used #include <iostream.h> instead of #include <iostream.h>
  • Unit tests were unheard of
  • Test-driven development wasn't considered

I Wish I Knew...

Having reflected on some of the insights I had earlier, I've realized many different things that I wished I knew when I started in school. I almost think they should have been obvious, but I had no mentor to ask and no prior knowledge to inform me. Here's a few of the things I wish I knew long ago:

  • Computer Science is not a programming degree. Although much of what is learned in computer science is useful when programming, knowing everything they teach in CS doesn't make you a good programmer.
  • Programming is an art. Programmers need a sense of aesthetics. They need to see how different pieces fit together to form a beautiful and functional whole.
  • Good code is beautiful code. Just as a good writer's sentences are flowered with adjectives and adverbs that form vivid images in his readers' minds, good code helps its reader understand and envisage the whole intent of the code. Each variable and name is like an adjective or adverb that adds clarifying definition to the whole.
  • Programming is rarely engineering. Unfortunately perhaps, requirements are rarely what they seem. A good programmer communicates, questions, and suggests whatever might be the best for her customer; she doesn't assume the customer knows what she wants.
  • Universities can't teach you everything. And, they don't even try. A CS degree program doesn't try to teach you everything you'll need to know, because much of it can't be taught. At best, the professors can encourage the student to go off and do research on their own. It takes hard work, and lots of practice to be a good programmer. Although you need not go through trial and error to make a test pass, it takes trial and error to perfect your skills, and there's nothing that you can do about it. Put in your 10,000 hours. Do it fast, and do it right. Read, study and learn on your own.

Countless other details could be added to the list above. I'm sure there are many more which I have yet to consider, but I hope this will help some future student.

What do you wish you had known?

[Image courtesy Guy with the Bolex on Flickr.]

Saturday, January 23, 2010

Eliminate Primitive Obsession

Primitive obsession is not just fiddling bits, playing with ints, or tossing around chars. Primitive obsession is using something non-expressive where something better suited is possible. It's about using List and Enum when domain objects would be far better.

In my post "The UI/UX Difference" I mentioned implementation models -- a system wherein the implementation leaks through to the user, forcing the user to understand something about how the system is implemented before it becomes usable. It turns out, I've been doing the same thing in my code.

Primitive: "1.a - not derived, 2.c - belonging to or characteristic of an early stage of development

I'm guilty. My code has too often been nothing more than a code implementation model. How's that you might ask?

Let me explain. Consider the following code:
createUri(
    annotation.getArgument(2)
        .getString(AnnotationConstants.SeparatorDefault)
    )

So now, my question to you -- what does that code do? You probably can't tell me. What if I had written the following:

separatorAnnotation.separatorStringOrDefaultValue().asUri()

The above code should make it much more obvious -- it's converting the separator string, or its default value, in URI form.

The first form uses a general annotation object, one that isn't derived enough. It forces the user, in this case a programmer, to:
  • know that the separator string is the third argument in the annotation, and 
  • know that each argument is an argument object, and 
  • know that the argument object can be converted to different types, and 
  • know that the argument should have been a string type, and 
  • know where the default value for that annotation parameter was specified.

That's bad, horrible, and awful! I'm forcing all these things upon other programmers because I'm failing to work with domain-level objects.  In this case, the separator annotation is a fundamental part of my domain and my business.  And yet, by working with a generic annotation object, I left it a primitive.  I left it in an early stage of development.

Don't make the same mistake I did.  Primitive obsession is much more than using a bunch of ints, it's about avoiding your domain in code. Use domain code and free others from understanding the implementation.

Saturday, January 16, 2010

The UI / UX Difference

As a child, either I or one of my siblings had a conversation with my mom that went something like this:


Mother: Where's that magazine you borrowed?


Child: Oh, it's right under my math book under that stack of clothes.


Mother digs through stack of clothes.


Mother: Amazing! I don't know how you could find anything in here.

When we create something, it becomes second nature to us.  We understand it; we know how it works; we know where we put things. What may have taken the Mom hours to find, the child could do in seconds.  The same problem exists with user-interfaces written without attention to the user.


Integrated Knowledge

As programmers and software developers, some things are instinctively easy to us.  Actually, it goes beyond that, we have habitualized and integrated every related metaphor and paradigm. Computers, applications and their uses are so ingrained in us that it becomes impossible for us to imagine life and computers without that knowledge.

Last year my dad asked for some computer lessons. My dad has no experience with computers -- he had never been interested in computers nor had he touched them since he was in college. And, even then, he was learning some basics of punch-card programming. Certainly not relevant any more.

My wife both laughed and shuttered as I attempted to teach my dad the basics of computers -- menus, toolbars, the difference between a right click and a left click. I tried to teach the generic concepts that would be applicable to most programs.  I was attentive and careful to use only universal idioms and metaphors.


I failed. He didn't understand the metaphors; he didn't see the relevance to life. Every concept was completely foreign.


Importance of UI / UX

I had recently been researching some user interface design issues in About Face 3: Essentials of Interaction Design so I understood a one very important thing:


Most everything we attempted was based on implementation model, i.e., rather than being goal directed and based on user understanding, my dad would have to understand something about how the computer was implemented in order to use the computer.

Resolve to Learn

Admittedly, it's impossible for me to forget everything that I know, but it's not impossible for me to learn much more about how non-programmers and every-day users think. I'm by no means a capable UI / UX person (quite honestly, I'm awful with colors and aesthetics), but I have learned enough to make a difference for the users of my programs.  Resolve to learn some UI and UX basics -- you can make a difference.

Inspired by a tweet by David Siegel.

Thursday, January 7, 2010

Think like a Master

Not too long ago I was reading "The Unreasonable Effectiveness of Mathematics," by Richard W. Hamming, the creator of Hamming Code and winner of the Turing award. Amidst many of his great comments stood one that transcends mathematics and fits into the lives of each of us, no matter what our professions or hobbies:

Not too long ago I was trying to put myself in Galileo's shoes, as it were, so that I might feel how he came to discover the law of falling bodies. I try to do this kind of thing so that I can learn to think like the masters did-I deliberately try to think as they might have done.



Do we take time to think like those who came before us? How did Galileo consider gravity? How did Pythagoras develop his well known theorem? How did Isaac Newton discover the inverse square relationship based on Kepler's laws?

I often look back in time and stand amazed at what my forefathers accomplished.  They didn't have the resources I have available to me -- the books, science, technology, and tools.  Yet, despite their limited resources, they developed incredible knowledge and wisdom in diverse and wonderful topics.

Think Like a Master

In order to glean a few insights from the masters, attempt to think like them.  I'll propose a few for programming and software development:
  • How would you encode the rules for a chess game? Could you write basic AI for a chess game? Now do both of those... and make the program work in less than 1k of memory.
  • How can you optimize reading and writing of files to extremely slow disks in the era of Microsoft Word 1.0?
  • How could you write a single-pass compiler that allows mutually recursive declarations?
  • What could you do to reduce the risk of an error when using a punch card system?
  • How do you avoid parsing an extremely large XML file to read the last N entries in the file?

The human mind is amazing.  It can imagine and discover wonderful things.  Take time to think, to ponder, and to learn from the masters and what they accomplished.

Tuesday, January 5, 2010

SMART Tests Outwit Bugs

With the new year come the typical thoughts of goals and resolutions.  I have come across the SMART acronym many times.  It states that goals should be:


Specific
Measurable
Attainable
Reasonable
Timely

After some thought, I realized that this also applies to tests.

Specific - A test should target one specific feature or behavior. The name of the test should be representative of what that behavior or feature is. When I name my tests in this way, as soon as the test failure is seen, I know what's broken. By looking at a few lines of code, I should know where the cause of the failure is. If I can't tell by looking at the code, my test is not specific enough. This also relates to the one (conceptual) assert per test idea. Like the high-contrast target above, you want to be able to tell, instantly, what went wrong.

Measurable - If you can't measure what you're attempting to test, it's pointless. For example, I don't test that the screen is drawn pixel perfect. It's not easily measurable. To attempt to test it would be a waste of my time. Manual tests are still tests; use them. TDD isn't a panacea.

Accurate - I consider a test accurate if it meets two requirements. First, it's name must represent the behavior being tested. Second, I must be able to look at the test and very quickly ascertain that it does what it says it does. Uncle Bob Martin's book, Clean Code, describes a good function as one that has a single abstraction -- one that can't be refactored any further.

Responsible - I shouldn't have to say this, but tests should NOT have side-effects. Tests with side-effects destroy any confidence that a later test may, or may not, be correct. Don't write tests with side effects.

Timely - Long-running tests are less-run tests. If a test isn't fast, I'm not going to run it as often. And, when I do run it, I'm likely to get distracted and start answering lots of Stack Overflow questions while I'm waiting for it to run.
Write SMART tests. By doing so, it becomes much easier to outwit bugs.

[Image from: http://www.ctc.com.pt/fotos_20061007.html]