Archive for the ‘Best Practices’ Category

Reading and Comprehending Your Way to the Top

Wednesday, September 30th, 2009

Let me start by saying that reading code is rarely fun.  Understanding it is hard.  Let’s assume that is a given, and avoid all the arguments that surround that topic.

Unfortunately, if you are a software developer, reading and comprehension skills are critical to your professional success.  Maintaining legacy code, integrating third-party components, even simple teamwork requires a developer to read and comprehend the execution of code written by a different developer.  Most real-world projects are simply too large to exist solely in the hands of a single developer, and if it did, the risk to that project would be so large as to be untenable for a responsible company.

Given the seemingly critical nature of reading and understanding code written by other people, why are we content to dismiss it as hard?  Writing software is just as hard, but no one seems to wave their hand at that problem as too difficult.  Not only that, but we talk about writing code for others, but never seem to discuss reading other people’s code (try searching Google for “writing readable code” and compare it to the results for “Reading Code”).

This seems to be a fundamental deficiency in the training of future software developers.  When we learn to write, we do not just sit down and memorize the rules for sentence construction along with word definitions.  Instead we balance those grammar rules by reading the works of authors who have already mastered their craft.  We read books, newspapers, magazines, journals, and develop an ability to recognize “good” and “bad” writing, independently of the actual validity and specifics of the sentence and paragraph construction.

Why can’t we do the same thing in software?

I’ve already talked about differentiating yourself by becoming comfortable in a domain outside of the development world, but that doesn’t always translate to your next project or job.  So if you want to differentiate yourself in the software development world, learn to read code.  Learn to recognize well-written code and poorly written code.  Learn how other people approach problems and construct solutions.  Your own work will improve immensely as you learn to recognize your own good and bad designs.

Reading Code without brackets

Monday, May 18th, 2009

Quick question, what is wrong with the following code?

bool result = false;
if (test1) {
  if (subtest1) {
    DoSomething();
  } else
    DoSomethingElse();
  result = FinalMethod();
} else if (test2) {
  result = GetSecondResult();
}
return result;

Stumped?  What if I told you that FinalMethod should only be called if “subtest1” is false?

I have changed the names and circumstances to protect the innocent, but the facts of the error remains the same as one i just encountered in a code review.  Of course, even knowing that little tidbit of information, it still took me a couple of minutes to see the error (Fortunately my spidey sense was tingling, so I knew *something* was wrong…)  The first time I read the code, this is what I saw (I reformatted to make the misperception clear, but the formatting was in reality the same as above):

bool result = false;
if (test1) {
  if (subtest1) {
    DoSomething();
  } else {
    DoSomethingElse();
    result = FinalMethod();
  } else if (test2) {
    result = GetSecondResult();
  }
}
return result;

Of course, that is a nonsensical statement, but that is how my brain interpreted the block.  Since the code was missing the brackets around the else statement, my brain simply supplied the expected syntax, regardless of its validity.  Given that the error was an introduced error, it is safe to assume the original developer’s brain did much the same thing, since there was even a comment to the effect that if subtest1 failed it would return the result of FinalMethod().

So how can we prevent these types of errors?  If our brains are actively working against us, then we need to work to give our brain what it wants:

bool result = false;
if (test1) {
  if (subtest1) {
    DoSomething();
  } else {
    DoSomethingElse();
    result = FinalMethod();
  }
} else if (test2) {
  result = GetSecondResult();
}
return result;

Now there is no need for our brain to supply the missing brackets, since they are explicit.  In addition, if we had originally used those brackets, it would have been immediately clear to anyone reading the code that the call to FinalMethod was occurring outside the else block, making the error much easier to detect, and much more unlikely to enter the codebase.

Standing out in a Crowd

Monday, April 13th, 2009

How many of you have ever commented on a manager’s lack of technical ability and/or knowledge?  I’d wager that most of us have been astonished at least once in our careers by the technical incompetence of someone in charge of a technical project.  A manager who doesn’t understand what “compile” means, who can’t grasp the concept of “automated testing”.  Someone who doesn’t understand the domain of their employees.

It is difficult to work for someone like that.  It can be difficult trusting their judgment when you know that they can’t grasp the simplest functions of your day-to-day job.  Now, before you think this is another internet rant about clueless managers, let me ask you a question:

Can you have a conversation with your customers in their domain? 

If you write financial software, can you sit down and talk with the financial analysts who use their software, without requiring the use of a “domain expert” to translate for you?  If you can’t, you should consider learning how. 

Too often we focus on learning our domain.  We learn all the neat tricks in our chosen language.  We become experts in the quirks of automated memory management.  We learn about the differences between allocating on the stack versus on the heap.

Honestly though, when was the last time a successful release hinged on some arcane development trick that only 10 people in the world know about?

Every software release hinges on the ability of that software to solve some pressing need of its customers.  And unless you are selling software and services to other software developers, those customers do not care about the software development domain.  They care about the financial services domain.  They care about the medical domain.  They care about the cellular phone domain.

Become a domain expert.  Learn to talk to your customers so you can anticipate their needs before they do.  Don’t be content to be just an expert developer, strive to be more, so that the next time a customer calls you, you don’t become that clueless manager, asking what exactly it means to compile your software.

Adventures in Remoting – Part 2

Wednesday, April 8th, 2009

In my last post I covered some of the lessons I have learned during my time spent telecommuting.? Today, I want to focus on some of the things my team has learned dealing with a remote version of me.

Integrating Remote Employees

  • Communicate, communicate, communicate.
    • Sound familiar?? If you think your team is communicating enough to the remote employee, you aren’t.
  • Have the employee start in the office.
    • The team needs to time to integrate a new member, and that is best accomplished locally.? Before an employee is allowed to telecommute full time, they should be required to work locally for at least a month so that everyone can meet and learn to work together without the additional burden of being remote.
  • Video-conferencing works when travel doesn’t.
    • Obviously, it would be preferable to have the employee local for meetings and so forth, but that can be prohibitively expense and time-consuming.? Install and use a reliable video-conferencing system, along with virtual white-boards and any other program that can make the meeting virtual.
  • Ad-hoc is not a process.
    • Dealing with place- and/or time-shifted employees amplifies any and all randomness in your process.? If your build is not automated, it will break.? If your code is not controlled, it will be lost.? If your tasks are not prioritized, your release will be late or won’t have needed functionality.? Your process must be able to handle the absence of hallway conversations.? If there is ambiguity or if it is too onerous, it will not work.? Be prepared to tweak, modify, and retool your process to meet your needs.
  • Define roles and responsibilities up front.
    • Without a clear delineation of who does what, there will inevitably be duplication of effort.? This is amplified when a remote employee may not discover the duplication until several days later.
  • Take every opportunity to include the remote employee.
    • Remote employees are just as important to your team as your local employees, so include them in everything.? If the team is celebrating a milestone, ask your remote employees to attend.? Cover their time and costs.? Treat them as if they are local and offer them the same opportunities the rest of the team has.

Obviously, this is not a complete list, but I have found that when my team observes these simple points, the whole experience improves.? When one is missing, the experience quickly devolves into unpleasantness for everyone.

Adventures in Remoting – Part 1

Tuesday, April 7th, 2009

In my day job, I have one of the perks that many folks dream of – I telecommute full time.? Working remotely (I am not even in the same time zone as the main team) requires a level of discipline from both the remote members and the overall team that most groups are not prepared for.? These are some of the things I have learned.

Working Remotely

  • Communication, communication, communication.?
    • If you think you are communicating the right amount, you are not sharing enough.
  • Who pushes you to excel??
    • You have to learn to work and be productive through the periods when you might have other distractions or the rest of the team may not have enough work to occupy you.? You need to be able to help the team towards your goal even when the team may not know what to do next.
  • Work space vs. personal space.?
    • This is not just the standard “have a private room with a door you can shut” advice.? Use a separate work-only laptop.? Log into a work computer remotely via VPN.? Create a virtual machine that contains your work configuration.? When you are telecommuting, you should be working on the same configuration as if you were in the office.? This improves compatibility with the rest of your team, and helps reduce the temptation to play just one more round of solitaire.
  • Manage your career.
    • Telecommuting full-time means that you do not have as much of an opportunity to build contacts and the relationships that are necessary to excel in your career.? This means you must focus that much more on managing and building those professional relationships.? Try to make travel on-site as often as possible to remind the team that you are an actual person.? Join professional groups and attend conferences.? Make yourself indispensable.? Work with your manager to develop a plan and make sure that you follow through.? Update frequently with your manager to ensure he/she is doing the same and nothing has changed.
  • Attack problems when they occur.
    • If something is wrong or bothering you, attack the problem head on.? Do not rely on someone else to notice and fix the problem because no one will.? Learn to deal with people in a straightforward, honest manner and learn to accept criticism.?
  • Prepare for change.
    • Unless you and your team has an established history of telecommuting, there will be growing pains which necessitates change on both sides.? Processes and roles will be updated and modified.? Privileges will be added and removed.? The organizational structure of the team will change.? Be proactive in making these changes, even if you do not know exactly what to do.? Experiment and admit failure if it doesn’t work out.? There is no set of magic rules and processes that will work for every team and every situation, so commit to finding those roles and procedure that work for you.

I can’t admit to always following all of these items, but I have found that when I do adhere to these lessons, things tend to go much smoother.

Bad API’s

Monday, March 16th, 2009

Bad API’s are the bane of every programmer’s existence.? They sap productivity and introduce errors into otherwise clean code.? They should be eradicated at all costs, yet they live on despite the best efforts to improve them.

How can you stop bad API’s?? The best way is to not write them in the first place.? This means paying attention to your design and paradigm.? This means taking the extra time to think about your problem.? This means that you will not be the first one across the finish line… at least for this race.

At least you can take comfort in the idea that some day some programmer won’t curse your stupidity for cleverly calling your simple AddItemToEnd method PutTheJunkInTheTrunk.? And they won’t throw things after discovering that you have to remember to increment a global count variable every time you use a factory to create a new object.? Trust me, it won’t be nearly as amusing after 10 hours of debugging, and it will be much harder to defend that decision.

Rejecting Defects

Thursday, March 12th, 2009

Not all defects are created equal – sometimes things are actually by design.? Sometimes the problem was a one-off type error that can’t be reproduced.? Sometimes the person reporting the problem simply gets it wrong.? For this reason, every project I have ever worked on has had some mechanism for rejecting defects.?

There are generally three possible reasons a defect is rejected.? The first is that the problem cannot be reproduced.? In this case, the rejection is done by either the testing staff (if defects are vetted before being assigned to a developer), or by the developer assigned to the defect.? A large number of non-reproducible defect reports is often a symptom of multi-threading issues.? Sometimes though, weird things just happen, and the effort required to protect against the 1 in a million chance of it happening again is too great to warrant attempting to fix the problem.?

The second reason is that the problem identified is actually correct behavior.? For these rejections, it usually requires a domain expert to make the appropriate judgement.? If a large number of rejections of this type are being seen, it usually indicates a usability error since users are not able to determine “correct” behavior.? Often rejections of this nature will spawn secondary defects or new requirements centered around improving the interface that caused the user to believe there was an error.

The final reason a defect might get rejected is because the defect is no longer relevant.? It has been Overcome By Events (OBE).? This rejection is a favorite of developers, and is usually cited when a consistently reproducible item suddenly stops being reproducible.? This is related to the first type of rejection, but in this case the defect is not as transient as in the first case.? In this case, the defect is a known problem that ceases to occur.? This can happen for a variety of reasons, especially in tightly coupled code.? A large number of these types of rejections is often a sign that development may be getting out of control and that unrelated changes are affecting code in other places.?

Sometimes developers like to “pad their stats” by marking items in cases 1 and 2 as fixed instead of rejecting them.? They like to have these problems show up as being fixed instead of rejected under the belief that since the problem no longer occurs, it was “fixed” by something.? I don’t like this method because it obscures actual changes being made to the code base.? This artificially inflates the number of changes that a given version appears to have had made to it.? Conceivably, if one were unscrupulous, you could create an entire release without doing any work simply by saving all these items in the work queue and then marking them as fixed all at once, giving the appearance of a large number of improvements when the code did not change at all.

However, I am assuming that rejected defects are subject to some sort of review process to ensure the rejections are appropriate.? Generally, this falls to either the testers or the customer, and they simply need to validate the explanation given for the rejection.? If the problem can be reproduced, or if there is continued disagreement, the defect should be re-opened and sent back for further review.? Otherwise, rejection becomes the resting place for all the unpopular changes and requests.

Custom Event Best Practices, and Why I am Ignoring Them

Thursday, February 19th, 2009

I’m a firm believer in best practices.? They provide invaluable guidance about solutions that have worked and failed in the past.? Following them provides other developers an easy cue to understanding your code.? Whenever I see code that ignores these best-practices, I tend to treat it with more suspicion than I would code that follows the established practices.? This is why I decided that I should probably talk about a “best practice” that I do not like, and have after following it for a long time, have decided to forego for my latest project.

According to Microsoft, when creating a delegate for an event, they advise you to follow the same EventHandler pattern the .NET framework uses.? This pattern required you to create an event delegate with a return type of void and two parameters – a “sender” parameter of type Object, and an “e” parameter which should either be of type “EventArgs” or derived from it.? I have even propagated this pattern when I talked about the potential for a .NET memory leak based on the way the framework handles event subscriptions.

So what exactly is my problem with this pattern?? It has worked well for so long, it seems kind of silly to revisit it now, but to be honest it suffers from one major flaw in my opinion.? Essentially I do not believe that the “sender” parameter should be of type object.? I believe that it should in fact be strongly-typed, just as you would strongly-type any other parameter in your application.

After working in C# for a long time, I have encountered 2 methods of dealing with the “sender” parameter in events.?

The first method is to ignore it.? When a developer creates a custom event, he or she includes all the needed information, including the strongly typed instance of the event sender, in the derived EventArgs class.? In the handlers, the developers simply ignore the sender as if it was not a useful piece of information, even though they are frequently accessing the sender via the EventArgs class.

The second method is the old cast and test method.? In this method, the developer knows that the object sending the event is interesting, and so the first thing he or she does is cast it to the expected type and test if the cast succeeded (or catch any casting exceptions depending on the method used to cast it).? Once they have a strongly-typed version of the sender, they can continue with handling the event appropriately.

Having encountered these patterns too many times, I have decided in my latest project to use a pattern which makes more sense to me.? In this case, that means that I have strongly-typed my “sender” parameter to the appropriate type.? This way I can use this information directly in any handlers I create, instead of having to create a custom EventArgs class that wraps information I should already have access to.

I have done some searching but have been unable to find any good reason for the generic sender pattern other than the fact that anyone can raise events, leading to the possibility of needing different types for the same event.? However, in my case, the benefits of having a strongly typed sender seem to outweigh the possibility of my wanting to use a different type to raise the event later.? So far it has worked far better than I had hoped and has allowed me to write event handlers that are more explicit in what they are accessing (at least to me).

Task Estimator Design Iteration #1

Wednesday, January 28th, 2009

I've been forced to step away from this for the past several days because of work commitments with my real job, but I wanted to take a moment to throw up (pun intended) the first iteration of my design for the task estimator.? I don't actually like it at all, so expect to see further iterations coming soon.

I've left a number of things out – notably the serialization and GUI – but this is the core engine as it exists right now. Ugly, huh? This is about as far as I've had a chance to take the design so far due to other demands on my time, but I remain ever hopeful that my schedule will free up “in just another day or two”… Of course, it looks like I'll be traveling again next week, so maybe I'll have a chance to work on it then.

If you are interested, I have been working on the design in Visual Studio 2008's Class Designer tool.? It is very cool, and very helpful since it will automatically generate these classes for you.? Plus, with the addition of the Modeling Power Toy, you can export the image out as an image map, as I've done here.

If you are using Visual Studio and aren't using this tool yet, for shame!? All you need to do to get started is add a class diagram to a project.? Visual Studio will automatically analyze the project and create the diagram based on your existing code.? Of course, you can also start with an empty class diagram and do all your class design work from there.? That is what I'm currently doing.? I find that it is much easier to change my class design and structure when I am simply editing a diagram.? When I have to actually dig into the code to make these types of sweeping changes, I find it much more difficult to resist the code's inertia (things tend to be “good enough”)…

Are you sure you want to exit?

Tuesday, December 30th, 2008

Why is it necessary to make me confirm every action I take?? Every time I see a dialog asking me to confirm something, I inevitably just hit return or click through the dialog without thinking, so what is the point?? On top of that, I am now trained to click through dialogs, so on the rare occasion where the dialog is useful (the Unsaved Changes dialog is always useful, even if it can be annoying sometimes), Often I’ll click through without paying attention.

This is bad.

Software should be written so I don’t have to confirm things.? If I screw up, I won’t realize it until after I’ve confirmed the operation anyway, so why not just provide me a way to undo my change so I can get back to my previous state.

Save the confirmation dialogs for things that are irreversible, like closing a document that has unsaved changes.? If you assume your users are idiots, I guarantee you will have idiots for users.? Treat them like they know what they are doing, but provide a safety net for when they make a mistake, and your users will be empowered to learn things for themselves.