Ode to The Code Monkey

 Yaacov Apelbaum-Code Monkey  
The Code Monkey (inspired by A Dream Within A Dream by Edgar Allan Poe)

Take another slap upon the cheek,
While slaving on this project, week by week. 
You have been wrong to work so hard,
Expecting riches and managerial regard.
Grinding out functions awake and in a dream,
Will not fetch rewards or professional esteem.

What you lack are not more lines of code, 
Rather it’s architecture and a road.
To substitute quality with speed,
Is the motto of the code monkey creed. 
You who seek salvation in RAD extreme
Will find, alas, a dream within a dream.

If you examine your latest stable build,
You will notice many bugs that haven’t been killed.
Strangely, they seem to grow in relation,
To your oversized code base inflation.
So many new features! How did they creep? 
Through scope expansion, they trickle deep.

Building good software is hard to manifest,
If you fail the requirements to first digest.
The lesson to learn from this development ditty,
Is that no matter how clever you are or witty,
If you fudge the schedule and estimation phase,
There is but one reward for you. The death march malaise!

© Copyright 2010 Yaacov Apelbaum All Rights Reserved.

Designed for Humans

Yaacov Apelbaum-Designed for Humans

In my previous life, I was a civil engineer. I worked for a large power marine construction company doing structural design and field engineering. The work assignments were pretty interesting. I got to blow up a bridge, salvage a sunken vessels, and build a lot of interesting marine structures.  On one of my projects, I was given the responsibility to design a set of beds for pre-stressed concrete piles.  The design challenges in this project were significant. We had limited real-estate and the loads involved were higher than any previously attempted.

Yaacov Apelbaum-Prestressed Concrete Piles Beds for pre-stressed concrete have massive anchors on each end. Between them steel forms are placed and steel cables are strung.  The cables are first tensioned, then the concrete is poured into the form.  When the concrete hardens, you cap the cables and cut them.  The result is a pile or a girder that is significantly resistant to various loads.

Following best engineering practices, I completed a structural load analysis document, a set of production blueprints with full dimensional drawings, welding, coating and assembly instructions, a bill of materials, and even a balsa scale model to help the manufacturing facility to visualize my design. Yaacov Apelbaum-Prestress Bed Scale Model

I was proud of my hard work and I felt that it was a great achievement.  The day before the presentation, I went over all the calculations again and rehearsed my slides.  After one last sleepless night, I arrived to the conference room to find several structural engineers, the yard superintendent, a number of field engineers from several divisions, and the chief engineer from corporate, an elderly white haired gentleman in his mid-sixties.  I remember feeling confident in my ability to sell my design to them.

The entire presentation went off without a glitch.  There were some stylistic comments but the overall feedback was good.  After the presentation, the chief engineer stopped by, shook my hand, and said that he liked my design very much.  Then with a straight face, he told me that he expected to see two additional alternative designs before we finalize our decision.

I was speechless.  “I’m not sure I understand, sir” I said. “Didn’t you just say that you liked the design?” I pointed out that none of the participants had found any flaws in my proposal.  “Why”, I asked, “did you think we need to develop two additional designs?”

He paused for a moment, and then said, “You never know what the best idea is unless you compare several good ones side by side.” I nodded politely, but I was disappointed. I felt like this was probably some form of engineering hazing. Was it truly the case that it’s impossible to achieve reasonable quality on a first try? I didn’t really understand how valuable his advice was until years later.

Yaacov Apelbaum-Pre-Stressed Concrete Bed

Completed pre-stressed concrete beds

Fast forward several years. I switched from civil engineering to software development.  At the time I was working as a lead front-end designer.  One of our key customers hired us to migrate a large VC++ client to a browser application.  In the mid-nineties, rich browser based clients were relatively unheard of.  We were stumped. Problems like session security, persistence, and lack of basic GUI controls seemed Insurmountable.

During meetings, I would regularly sketch various GUI solutions.  But I often found that as soon as I came up with a solution, a new set of problems would be exposed and a redesign would be necessary. In retrospect, most of the ideas I came up with at the time were sub-par. But with each design, no matter how bad, another potential solution was discovered.  Each new design I sketched out was closer to the solution than its predecessor. Even the poor designs peeled away some layers that obstructed the problem that I didn’t initially see.

After dozens of attempts, I had an epiphany and came up with one design that it was possible to implement in several ways. Sketching and contemplating the various designs helped me tremendously, but when the time came to present my solution, I made a tactical mistake. I deliberately neglected to show all of the other working ideas for fear that they would think that I was a mediocre designer; why else did I need to work so hard on so many designs just to yield one single decent one.

I realized in retrospect that there would have been any number of acceptable designs and by not presenting some other ideas I considered before arriving at the one I chose, I short changed myself. If anybody had suggested one of the other options I had discarded but not mentioned, I would have had to explain that I had already discarded that idea. But at that point , it would jeopardize my credibility because it would look as if I was only trying to brush them off.

 Yaacov Apelbaum-Poor Design   Yaacov Apelbaum-Quality Design M1917

Multiple product designs

After participating in and leading many painful design meetings, I have come to the realization that the best way to sell the top design idea is to first share some of the alternative and inferior designs.

If you are responsible for usability or user interface design, you have to develop at least several alternative options for credibility purposes. By that I don’t mean that you should become a cynic and create duds just for the sake of generating volume. The alternate ideas have to represent meaningful and functional choices.

Once you have your alternates worked out, walk through the various options during your design meeting and call out what the pros and cons are for each and what the overall solution trade-offs would be. When discussing designs, place emphasis on both the positive and negative qualities of each alternative.  This will help your peers view you as an unbiased and professional presenter.  Also, you may find that when you present your top candidates, your team will come up with some hybrid solutions that otherwise would have been impossible to generate if you had only presented a single one.

Nowadays, I am often tasked with working on problems that are exceptionally difficult to overcome (with respect to both technology and schedule) and the typical, off the shelf solution is just not sufficient. But there is hope. Usually after a few days of intense interterm deliberations complete with often heated exchanges of alternate designs, magic happens.

My secret sauce for breaking down the most difficult design problems consists of the following steps:

  • Get your entire team into a conference room, order plenty of pizza and write down all possible solutions on the whiteboard. Make sure that everyone offers an opinion. Don’t make any go-no-go decisions during your first meeting; rather leave the information on the board for several days (don’t forget to mark it as ‘do not delete’)  and schedule a follow-up meeting. Tell everyone to document the pros an cons list for each option and provide specific use cases.
  • Get your team into a conference room a second time, order plenty of pizza and write down the pros and cons list for each choice.  Boil down your choices to the top three candidates.
  • Work out the feasibility of each of the top three candidates and cast a vote for the best one.  This is the time to establish consensus and a team buy-in.

Way back when the chief engineer asked me to come up with two additional alternate designs, he was in fact telling me that no matter how talented a person is, there is tremendous value in variety.  He was also saying, that in order to come up with a ‘good’ design there must first be several inferior ones. If you are responsible for the design of any product futures, you will want to encourage your team to flesh out the bad designs on the whiteboard or as POC, not in your final product.  Unfortunately, the only way to achieve this is by expending resources and time exploring several possible solutions, up to and including some unattractive ones.

A common development folly (see It’s Good Enough for me) is the notion that there is in fact a ‘best’ solution or one right answer to a given problem.  Actually, the opposite is true. Considering time and resources, in most cases, the ‘best’ possible solution isn’t worth the effort and a ‘good’ solution would more than suffice.

If you are curious abut which design I ended up using for the prestress pile beds, it was the third one.  It turns out that unexpectedly, after I reconsidered the problem again, I realized that due to the yard’s location at sea level, the water table was too high to accommodate my initial proposal. As a result, my updated design required various modifications in order to solve this problem.

Live, design and prosper.

 

© Copyright 2010 Yaacov Apelbaum All Rights Reserved.

Crafting Great Software Features Part-1

Yaacov Apelbaum-Useless Technology  
Driver’s Entertainment System and Password Protected Gear Shifter

Trying to do anything well is difficult. Developing useful features is no different. It takes more effort to create useful functionality than to produce eye candy.

Good feature design comes from a reliable and repeatable process (not dissimilar from CMM). Unfortunately, many organizations still have not figured this out and instead of investing in usability engineering, they slug it out in isolation and tragically, like Sisyphus, doom themselves to getting the wrong functionality and spend eternity (or until the project is canceled or runs out of money) patching their mistakes, version after version.

If your goal is to build a useful product, you should schedule your project so you can develop the needed functionality. Habitually excusing yourself by claiming that you “just don’t have the time” to develop quality is a cop-out. If you find that your team is spending a significant amount of time on feature seesaws (taking them out-putting them back), it means that you’re not planning well, and that the technical goals are not aligned with the product objectives.

Feature Framework
In a functional development organization, everyone should fully understands how their individual contributions will impact the end user. To help achieve this, there should be a feature framework in place.  The feature framework  must cross organizational lines and bridge development, PM, product planning, sales, and marketing.  The chief purpose of the framework is to determine what challenges end-users have and how a proposed feature, functionality, or technology will solve those problems. Without utilizing some degree of Feature Driven Development, you stand the risk of creating features that may look great (like password protected gear shifter) but solve no real problems.

Management Knows Best, Well, not Always…
In order for you to master the product usability question, you have to conduct real life tests with real would-be users. Talk to your customers about their pain points and ask them what they love (and hate) about your product.  The earlier you get the bad news, the better you’ll be in the long run.

Keep in mind that most failures in software usability can be attributed to poor decisions at the executive level, which are promulgated due to a culture of silence. Developers and designers should be encouraged to think critically about their work and be provided with official channels for expressing their opinions (in a non polemic manner).

Make sure they talk to the the other members of your team to broaden their horizons.  Invite their participation in every feature conversation. When considering new or revolutionary UI changes, solicit input from your customers and your organization.  Without sufficient end-user, business justification, and sales input, its easy to develop functionality that universally disliked (like the notorious Microsoft Clippy feature).

One of the most common development failures (after underestimating feature complexity and the cost and time to develop it) is the inability to correctly define the problem space and instead to develop solutions that are looking for a problem.  If the project objective is vague and not granular enough, it’s impossible to know whether it has been solved or not. Furthermore, even if the objective is well defined, you may still be working with the wrong assumptions about what the customer really needs.

A video screen built into a steering wheel that plays looped safety movies won’t help your user drive any safer. These two types of problems—vague objectives and the wrong assumptions—have nothing to do with your team’s technical ability. If you can’t side step these kinds of issues, even the best software engineers and designers are bound to fail. You may write great functionality and create whooping UI, but if you can’t solve the right user problem, all of your hard work will be wasted.

Do you have a Problem with That?
The first step towards critical feature analysis is to take an objective view on the nature of the problem. As a developer, you are inherently biased towards the value of your features and suffer from a certain degree myopathy. You’re inside the tech bubble looking out and so you cannot possibly see your creations the way your users do.

Yaacov Apelbaum-Team Structure To improve visibility, you need to supplement your view with external sources. These should include: UI team, technical developers, product business champion, actual customers, sales, training, and marketing.  Get as many alternative views as you can. Again, make sure to talk directly with the users effected by the design, Don’t take single  camp’s word for what the problems are. Think of yourself as a newly elected congressman trying to keep your constituents happy. Don’t only consider the opinions of the power lobbyists.

Another challenge is that the way approach your customers for feedback will impact the type of information you get from them. If you unintentionally bias your questions, you’ll get skewed data. The art of observing and understanding customers needs is an acquired skill and it may take several trial runs before it’s honed.

When researching the problem, keep the following key questions in mind:

  1. Who are your user’s, what are their skills and job functions?
  2. What is your user’s workflow and how does your product enhance it?
  3. Do your users use other products to supplement your app?
  4. What business assumptions are you making about your users and their environment?
  5. What are your user’s specific deliverables (spreadsheets, reports, presentations, etc.)?
  6. How do your competitors solve the same problems you are working on?
  7. What is your user’s strategic information roadmap and how does your app fit into it?

If you don’t have complete responses to these questions, you cannot start to design or develop any features or functionality. a solid understanding of your customers and the answers to these questions form the foundation of your application.

You Only Get One Chance, So Chose Wisely
As you are preparing to work on your next version, you will discover that there is an infinite number of issues that need solutions and a mile long list of most have features. But just having a raw list of bugs and features isn’t enough to build a product.  As it goes in life, some problems are not worth solving once you consider their poor return on investment and time.

Often, a solution to one problem spawns multiple new problems (and bugs). You need to exercise good judgment, which means having the ability to distinguish what should be done from what can be done. After collecting the business requirements, the next step is the development roadmap. You have to synthesize the requirements and create a specific action plan for where to invest your time and resources.

With the data collected from customers and internal sources, distill the information into short one-sentence lists. These sentences should be written from the point of view of your end-user. For example, “Enable password field to accept 11 characters” is not a problem statement. But “Password field must support strong passwords” is. The difference is significant. You rarely want to define the solution and the problem in the same statement: If you do, you’ll miss the real problem.

In this example, there may be many other ways to solve the problem of password strength, including hard coding the logic to accept 11 characters. But if you are too focused, you’ll never see the alternatives (make the password length database driven, integrate it with LDAP, biometrics, etc.). Good feature development is all about understanding your alternatives.

Yaacov Apelbaum-problem to a solution

A solution looking for a problem

For each problem statement, provide supporting information. Include details about which users have the problem, how it was discovered, and what the potential workarounds are. Determine whether the problem only occurs in certain configurations. Following Feynman’s rule of scientific integrity, provide as much details as you can to allow others to confirm or challenge your assumptions.

If you’re the owner of the usability study or market research data make it available to everyone.  Don’t ask anyone to “trust you”. The more open you are about your feature sources, the less likely it is that people will suspect you.

Hold your Fire
Only start coding when you see “the whites of their eyes”.  When the goals are set and the problems to be solved have been well defined and understood, you can begin to build the features. Instead of adopting a top to bottom approach (where the development team is told what to do), engineers and UI designers should have free rein to generate the ideas that will solve the problems. Time should be allocated to investigate different alternatives that might provide the necessary functionality, and to run usability studies on prototypes to see if they actually improve the end-user experience. Only once you evaluate all your potential solutions and pick the best one can you engage in full speed development. The rest of your solutions do not have to be disregarded; you can shelve them for future releases.

As long as you are working within a feature framework, you are guaranteed to be marching in the right product direction. There should be a lot of innovative and creative juices flowing in your team, and even if you can’t completely solve all of your feature functionality challenges, a partial solution to the most important problem will still be superior to a perfect solution to the wrong problem.

 

© Copyright 2010 Yaacov Apelbaum All Rights Reserved.

Developers Just Wanna Have Fun

 Yaacov Apelbaum-The Cinawaffle
The Cinawaffle DX250 Waffle Maker\DVD\MP3 Player is Remote Controlled and Bluetooth Enabled.  It Comes Standard with an SDK and a Built-in Web Server.

One of the greatest fallacies in software development circles is that great products must be made with cutting edge technologies.

This belief is not coincidental, as most of the people who work in high tech maintain a passionate love affair with technology. If you ask most of us about our willingness to work long hours on risky and challenging projects, the answer is likely to sound something like: “I love technology, it’s fun,” or “I enjoy playing with technology; I can’t believe that someone is actually willing to pay me to do this job.”

Hardcore techies aren’t the only ones who are enamored, It seems that since the ushering in of the industrial revolution, we all have come to embrace the utopian belief that technology will one day solve all of humanity’s problems.

Believing this, many seasoned business and development managers fall into a trap and tend to package unnecessary technology into their applications. Unfortunately, this is most often done to the detriment of the product and its users who fail to leverage or appreciate the complexity it brings.

I have witnessed numerous product launches where the entire company stood in awe at the sight of their own technical achievements.  Managers and developers alike were congratulating each other on overcoming what early on seemed to be impossible development roadblocks. All without any regards for the sketchy implementation and clear sight of the purpose that such functionality might serve.

Yaacov Apelbaum-Neo LudditeYaacov Apelbaum-Luddite Reward At the risk of sounding like a Neo luddite, I would argue that our infatuation with technology doesn’t always lead us to develop the products our customers need.

When you were interviewing for your last gig, you likely stated that the ability to work with new technology and learn new tricks was a significant factor in your decision to apply for the position. You may have also been told by your interviewer that his company prides itself on maintaining a fun work environment and that you would get the opportunity to work on some very cool and cutting edge projects.

I believe that personal and company success hinges on establishing a creative, unpretentious, and challenging work environment.  But that’s not the whole recipe for success.  Any organization that hires expensive technical staff cannot afford to do it for fun’s sake only.  A company can only sustain a playful atmosphere if an increasing number of paying customers embrace and consider its product useful.

The reason for this is simple. Contrary to the common misconception, it’s not angel funding, seed money, or bridge loans that pay for your operation. Rather, your end users end up picking up the tab for all of your company’s salaries as well as for the espresso machine, the new Wii\Xbox station, and the billiards table. In light of this, every activity you engage in (e.g. design, coding, or testing) must directly benefit your costumers and end user.

How granular are the effects of product usability on your daily work?  Each line of code you write, every bug you find, every new feature you consider must help the user improve his productivity in some quantifiable way. No matter how obscure or indirect your current project is, streamlining the user interface, improving application stability, or optimizing performance, all must directly benefit your customer. If you can’t clearly see how your work improves the user experience, consider investing your time on some other activity. The more frequently you think about your work in terms of end-user productivity, the more impact you will have on creating a profitable product.

The Beauty of Simplicity
The greatest engineering feats are the ones we don’t notice. The hallmark of a great designer is his ability to translate complexity into simplicity. The automatic transmission in a car represents significantly more engineering effort than a manual transmission, but it dramatically transforms the average user experience. The best engineering, architectural, and consumer products always focus on improving user experience and hiding complexity, not showcasing it.

The most effective approach to adding value to products is to add power and ease of use while reducing the learning curve. When you are developing a new feature, ask yourself, is there some way to add it without changing the user interface? (users hate learning new interfaces)  Can we solve a problem by re-designing workflow or consolidating instead of adding more screens and menus?  Or is there some other feature we can modify to include new and improved functionality? Think of car manufactures and how they add major new features with minimal user impact. The dashboard almost never changes, rear anti-collision video monitoring is integrated into the GPS display, anti-lock brakes are added to the standard brake pedal UI, and power steering is added to the steering wheel UI. Minimal training is required on the part of the user to reap the benefits of these new features. This kind of design approach—where complex functionality appears simple to the user—helps create great products.

There are countless opportunities throughout your application to provide a great user experience. Watch your customers usage of your top features and ask yourself how they compares to the level of service you’d get in a 5 star establishment. Is the product fast, intuitive and easy to use? Is efficient help always available promptly? Is it easy to get information out of it? And most importantly, does your product enhance the user’s productivity and business process flow?

Regardless of the technology you adopt, the closer your application’s usability matches the levels of service people get in their daily physical experiences, the closer you’ll be to having a great and useful product.

 

© Copyright 2010 Yaacov Apelbaum All Rights Reserved.

To Make Errors is Human, to Handle Them is Divine

Yaacov Apelbaum-We Have Bugs

Reading this advertisement made me realize just how clever the software industry has become.  Why bother fixing your bugs prior to shipment when you can sell it on the premise that you will fix the bugs “free of charge” when the users find them for you. Interestingly, anyone who bothered to read their licensing guide will find the following sobering caveat:

“…From an engineering point of view, it is impossible to fix bugs in multiple source code branches. If we would have to do this, we would never be able to implement a major redesign. Major redesigns are required now and then to be able to fix bugs and add features fast.” 

Nothing communicates your attitude towards your users better than the way you handle exceptions and errors. As soon as something goes wrong with your application the user is at a heightened emotional state and is the most impressionable. Some software products, including some of leading market applications, have developed a bad reputations for having cryptic error messages that are impossible to resolve, leaving the user feeling helpless and outraged.

The worst offenders include fortune teller style messages that inform you (not without irony) that you are about to lose all of your work because the application has encountered an unknown problem and needs to shot down.

Yaacov Apelbaum-Useless Error Message
A Useless Error Message

This is even more pronounced in the session-less environment of the internet. It seems that when it comes to web application reliability and robustness, we’ve been steadily taking a step backward in the way we treat our users.

Yaacov Apelbaum-Lost my Browser Yaacov Apelbaum-Stopped Working

Yaacov Apelbaum-Blogger Error Yaacov Apelbaum-Microsft Live Writer Error
Useless Error Messages

The Engineering Handling Failure
A civil engineer designing a bridge will invest a significant amount of time and resources in predicting potential structural failure scenarios. Failure analysis and safety factoring (i.e. redundancy) are two important cornerstones of the engineering discipline. In the physical world of machines and structures, the ability to identify a potential design flaw and remedy it is a given. Similarly, we should strive to achieve the same in the virtual software world by accounting for critical error conditions and developing robust application code capable of handling those cases.

Software engineering does have certain nuances that differ from classical engineering, which makes prioritization of work more arbitrary and less straightforward. For example, a memory leak in a server component may be considered by the development team to be a critical bug, but a relatively small data validation problem that forces the user to retype a lengthy application could have a bigger user impact and rank higher on the bug fix priority.

A 12 Step Program for Error Rehabilitation
Making your application more agile in handling failure and enabling it to degrade gracefully are not a single step processes and there is no silver bullet technology out there that will fix this problem.  If you want to break the cycle of application instability and user frustration, you will have to dedicate time and your best technical talent to solving it. I have found that a phased approach works best.  In this approach you first handle the low hanging fruits, (addressing the mechanics of the error handling), and than gradually move to higher ground (addressing automated problem resolution and preemptive countermeasures).

The following is my 4-phased program for solving your application errors. Classification is inclusive, so the 4th phase (the highest level of reliability) also includes the properties of the preceding levels:

Phase-1: Create Unique and Traceable Errors and a way to Record them
If you are under the gun and don’t have time for any other remedy, at least make sure that your error cases are unique. Telling your users that an error has occurred in the application without providing details is a sign of an immature product. When your technical support team receives an error report, they should be able to determine precisely what is causing the problem.

Generic error handling (same message for all errors), or different error causes that return identical messages, are easy to implement, but when it comes to debugging they are useless. Unique error IDs allow us to more efficiently track bugs and translate them to a more stable product.

Error codes should be visible in the error messages but not be the focal point of the the message.  You should develop a library of descriptive text that provides a human readable explanation of what the error means.  Provide a simple mechanism to either log the message directly into your app or send it to you via email.  Nothing is more annoying to the user than being asked to type in the error message manually.

Establish an Issue Tracking System that allows quick data entry and reporting.  At the minimum record the error code, error description, and the steps to reproduce it, effected environments, and its frequency.

Phase-2: Keep the User Calm and his Data Safe
Error messages should always carry a mature and responsible tone. Always use supportive, polite language, like a good teacher would when instructing a pupil.

If the user opts to leave a mandatory field empty, or mistypes the data type (CC#, zip , etc.), don’t go ballistic. Non-critical errors deserve non critical messages. Instead, indicate on the entry form where the problem was, place the cursor in the relevant field and leave the rest of the data intact. This is especially important for long entry forms that require a lot of effort to complete.

Don’t force the user to duplicate entry of some previously supplied data for verification purposes (such as billing and shipping information) as it may introduce human error and trigger him to abandon the application altogether.

Phase-3: Good Errors Messages are Clear and Provide Remedies
The way the user perceives the error is much different from the way you do. He thinks in business terms and knows nothing about the inner workings of your application, nor does he care. That’s why you should always design the error UI from the user’s perspective.

Here are the seven golden attributes of error messages:

  1. Describe the error in user terms and language
  2. Instruct the user as to how to complete the task and resolve the error
  3. Explain how to prevent the problem in the future
  4. Avoid technical mumbo jumbo and acronyms
  5. Avoid modal pop up error messages and instead write error directly to the page
  6. Provide help links that better explain the nature of the error
  7. Keep the text formatting simple and avoid bright colors and animations

When providing a solution, give clear step by step instructions as to how to fix the problem. Be specific and do not assume any pervious user knowledge. If there is a relevant tutorial or the specific solution in your on-line help, provide links directly there. If it’s a critical problem—for example, the Website is not accessible—provide a mechanism for the user to report the problem to you and immediately acknowledge the receipt of his complaint, provide an explanation and an estimate of time before this problem will be resolved.

Phase-4: Handle Errors Internally
Write code to robustly handle all errors. This will eliminate the most severe and common errors (like missing data or validation). You can achieve this by automating data entry components from the user interaction (i.e. deriving city name from zip code).

To the extent possible, take corrective action before an error occurs. For example, if the user is in the middle of a lengthy entry form, save the contents as he moves between fields, this will allow you to restore the information if he inadvertently navigates off the page or even closes his browser session.

It’s often expensive to identify and address all possible failure cases, but if you have been tracking your top bugs, you can start with the biggest offenders first.

The way you handle and communicate application errors directly reflects on your team’s and your company’s reputation. When building a new or reworking existing functionality don’t assume that the old error messages apply to your new logic and boundaries.  Building test cases around various error scenarios (missing data, wrong data, bad data, etc.) and dedicating a test cycle to generate all known error messages is also an excellent strategy.

Error handling and messages should be thought of as required phase of any feature development, and adequate engineering time for it should be budgeted into all SDLC estimates.

Real quality of service goes beyond just acknowledging your application’s faults. My rule of thumb is that there is no such thing as an “informative error message”. A good error is one that has been eliminated through error-handling code and through superior product design.

© Copyright 2010 Yaacov Apelbaum All Rights Reserved.

Drinking on the Job and Other Vices

Yaacov Apelbaum-Drinking on the job

Working in an early stage startup can be a blast. For me, it is a most rewarding personal and professional experience. There is minimal bureaucracy to get in the way and you have the opportunity to build the product of your dreams.

True, at times a few dark clouds (like the possibility of not making payroll) may gather on the horizon, but with some ingenuity you can handle it. If you are preparing to ship version 1 of your product, the pre-launch phase or the final sprint can be an ultimate adrenaline\caffeine rush, and the chronic lack of sleep will just enhance the experience.

Working in this environment may not be for everyone. The rapid pace of change, the seemingly never ending To-Do list, and the constant improvisational nature of the work often frustrate individuals who are used to more structured development. Financial stability can be another turn-off. Most early stage startups operate on a shoe string budget using seed\angel money that has to be stretched to the limit in order to build a viable proof of concept. Once in place, the funding and development cycles are repeated in the hopes of surviving long enough to attain commercial viability. These repeating cycles, if not executed properly, can become a tiresome soap opera.

To use an equestrian metaphor to illustrate the development effort in a large organization is somewhat similar to English-style show jumping competitions. A smartly dressed rider moves elegantly between the stations (projects), always maintaining aristocratic poise and composure (an eye on features, schedule, and budget). The same event in a startup looks more like a rodeo, where the inexperienced rider jumps on the back of a wild mustang with neither saddle nor reins and spends the duration of the ride screaming, swearing, and holding on for dear life (dwindling budget and product immaturity) while the horse (the competition) simultaneously kicks, bites and tries to throw him off. Net-net, a tour of duty in a startup can resemble the life of a Caribbean buccaneer. The risks are plentiful but the rewards of a buyout or an IPO can be glorious.

In addition to operational differences, startups also tend to have a more casual and festive corporate culture. To help recruit and retain talent it is not uncommon to offer employees various soft perks like no dress code, the freedom to play interior decorator, and access to amenities such as the latest video games, quality coffee, soft drinks, snacks, and other tech toys.  Some companies, in an attempt to even further sweeten the sub-par monetary compensation will go as far as to abstain from creating work policies regarding vacation, work hours, and employee conduct.

If you are on the management team of a startup, it is tempting to convince yourself that you can make up for low base pay and a high stress work environment with theme park playfulness (a approach not dissimilar from paying for prime real estate with firewater and glass beads). But in reality, trying to convince highly intelligent people that the privilege of working in a glorified techno-utopian commune is an adequate substitute for decent wages amounts to little more than Svengali’s operatic hypnotism, it may work for a while, but sooner than later the effects wear off, leaving you with consequences like high attrition rates and chronically missed deadlines.

Firewater and Glass Beads

In one of my engagements, I had the opportunity to work with a late stage startup. The company desperately needed to mature and commercialize its product and swing to profitability. This had to be done rapidly in order to secure bridge funding.  When I first took over the technology and engineering organization, I discovered that the team had an almost 50% turnover rate and that my two predecessors had left on very disagreeable terms. Initially, I was surprised that the subject of almost every staff meeting revolved around compensation, but that matter quickly came into focus. The developers were disgruntled because, in addition to being significantly underpaid, they hadn’t received their promised bonuses (which accounted for almost 40% of their salary) yet they were still being asked to work 70 hour weeks. It was evident that all the soft perks they were getting didn’t succeed in dulling their memory of the promise.

In another engagements, the CEO refused to budge on the question of base salary and missed bonuses and argued instead that work conditions (flex hours, free sporting event passes, telecommuting, and free alcohol) were more than sufficient  compensation. On the eve of the “Mother-of-all-Sprints,” (a hellish 320 hour coding month), some team members started exhibiting disturbing behavior that included sending inflammatory emails to the entire company (see excerpt below), rowdy conduct in staff meetings, and calling in sick.

Gentlemen,

As part of the “Mother of all Sprints”, we have been asked to go above and beyond our normal dedication. We have been asked to make major sacrifices in our personal schedules. We have been asked to work harder with longer hours. We have been asked to cancel existing plans we may have put in place with our families. We have effectively been asked to put our lives on hold until the end of the sprint. And we have been asked to do this without any advance warning. Furthermore, many of us disagree with the exact product direction.

  • We disagree on the functionality necessary for the company to go forward.
  • We disagree on what the bottlenecks and limits with the current process are.
  • We disagree on the feature focus of current efforts.
  • We disagree with the release date.
  • No one asked the development team what they felt was important to accomplish.
  • No one asked the development team what features they felt were necessary.
  • No one asked the development team what they they could realistically accomplish.
  • No one asked the development team when the release should occur.
  • We have no real equity in the company. We have no guarantee we will ever receive bonus payments. We have no guarantee that bonuses will be distributed fairly, if they are ever paid out.

So I have to ask myself, what is my personal motivation for doing this? What do I get out of it?

Of course, it didn’t help that the company offered its employees a steady supply of beer and hard liquor, and even had gone as far as openly encouraging everyone to drink on the job (at the ever so popular “Tequila Fridays”). HR wouldn’t implement any conduct policies because they feared that doing so would dilute the startup experience. Not surprisingly, some employees in turn responded by drinking just a bit to much and staying home sick.

Workspace structure and policies are as mandatory as social contracts and are designed to protect us from abuse and anarchy. Hobbes observed that life under the rule of the mob is “nasty, brutish, and short.” Similarly, life in a startup modeling its governance to resemble the “Lord of the Flies” is wretched and hardly short enough.

Unfortunately, no one has the exact formula for balancing discipline with productivity. But I have found the following rules of thumb to be an affective guide:

It may be contrary to your anti-establishment philosophy, but it is in your best interest to create and maintain an orderly work place. Unfortunately, the only way to achieve this is by formalizing rules for all employees and enforcing policies like performance reviews, bonus payments, vacation and sick time without favoritism or exceptions. 

Here are my six golden rules for keeping anarchy at bay:

  1. Provide formal policies regarding company usage of licensed software. It may be true that software yearns to be free, but unless it is properly licensed it should be kept off of company computers. This may sound picayune, but using counterfeit or cracked software is a huge legal issue.
  2. Regardless of how hip and progressive it may seem, do not encourage consumption of alcoholic beverages or drugs on company premises. Every garden has its snake and this one will bite you big time. It’s only a question of time before one of your employees under the influence of something he consumed at work will become a legal liability.
  3. Do not tolerate violent or abusive behavior (such as regular use of foul language or extensive absenteeism) from any employee, including leads and management.
  4. Offer your team competitive financial compensation. If you can’t afford to pay market wages, be honest and upfront about your limitations. Don’t try to renegotiate salaries down by arguing that industry wages are inflated.
  5. Be creative about company spirit and culture. Offer your team as many soft perks as you can, but remember, perks are not a substitute for wages.
  6. Don’t exaggerate or misrepresent bonus target amounts, stock option availability, or plans for employee profit sharing. Be forthcoming about the financial state of the company and its stability. My rule is “promise only what you can deliver” and in a timely manner “deliver what you’ve promised.”

Maintaining transparency and a fair work environment are two of the most important pre-requisites for a smoothly operating startup. Once these are in place, you will discover that you are well underway towards achieving your most ambitious development goals as well.

Good luck.

© Copyright 2010 Yaacov Apelbaum All Rights Reserved.

The Anti Socials

Yaacov Apelbaum-Anti Socials

I recently had an opportunity to discuss the question of social networks vs. commercial software at great length with a fellow airplane passenger who happened to be a SVP of technology in a fortune 500 company (which will remain unnamed here).

As we were preparing for takeoff on a cramped CRJ50, I took out my current reading assignment: The Emerging Science of Spontaneous Order. My neighbor sitting in 1D inquired about the subject of my book and during the course of the bumpy flight and the conversation that ensued, he bemoaned his inability to understand how relatively young startups the likes of Facebook, Twitter and Linkedin were so rapidly able to develop so much rich functionality and capture such a large market share, while other much more mature organizations with much bigger budgets and talent pools were failing to make any such inroads.

His frustration is by no means unique. It is a shared by many large technology companies such as IBM and Microsoft which at present are still scratching their head trying to figure out if this social networks thing is for real and does providing a communication platform for income-challenged teenagers makes any commercial sense. To see just how hesitant the traditional software cartels are to dip their toe in the chilly and profitless waters of the social media, examine the social network landscape. I challenge you to identify even one significant, viral product developed by any of the major software vendors.

One example that illustrates this failure to improvise, adapt and overcome is Microsoft’s difficulty in harnessing the emergence of blogging and SMS as commercially viable services. In 2004, after some soul searching, they concluded that it was unlikely that products like LinkedIn would be commercially viable because—they guessed—few professionals would agree to pay a monthly subscription for them. In 2005 even after it became clear that users were indeed moving en masse to open and free social platforms, Microsoft continued to insist that this was just a fad and what these users really needed in terms of social networking were minor improvements to the MSN subscription service, Office Live, and Windows live platforms.

For Microsoft and other leading commercial software vendors, social networking has to be a subscription based because their entire operating model is based on reoccurring revenue (either via subscription or licensing).

Yes siree, for big soft, it’s one of those ‘damned if you do and damned if you don’t’ scenarios. You may find it hard to believe, but the same organizations that made their fortunes outdoing each other with applications like the spreadsheet are now missing the train big time on what is clearly the next killer platform. In what appears to be a blockbuster sequel to Oedipus Rex, they are powerless to leverage this newfangled phenomenon to make any money or capture market share.

Microsoft is certainly not unique with regard to this model. AOL with its Messenger product is another good example of how to squander your entire user base. Following a slightly different tactic, they offered a “free messaging service” with the small caveat that the user would need to install a fat and intrusive client that would quickly pay for itself by monitoring all of your Internet (and network) traffic all the while serving up unwanted advertisements.

If you think that this form of myopathy affects the big software companies, think again. Traditional communications organizations Nortel which should know better (because of their proximity to the social segments) are still trying to survive the proverbial 7 lean years in the hopes that the public social network phenomenon is just a fleeting narcissistic fad fueled solely by generation X’s and Y’s fascination with exhibitionism. In their vision of market paradise, all future social networks will once again go back to being routed and controlled through their proprietary appliances and just like in the good old days they will once again skim the fat profits on a per-server\user license basis.

Whether you like it or not, the social networks are here to stay. They provide meaningful social interaction, are fun to use, and ultimately do what good software is supposed to do: connect people and give them more control over their lives.

My advice to big software is to heed Dylan’s words: “The Times They Are a-Changin”. Don’t wait any longer; jump in now and contribute to the social networks development effort by providing free and open source applications (Seadragon and Wave would be two great candidates). As contrary as it may seem to your operational philosophy, disregard the immediate profitability question and do it because of the tremendous social benefit these products could offer. I have no doubt that in due time, the money, champagne, and caviar will follow as well.

© Copyright 2009 Yaacov Apelbaum All Rights Reserved.

Playing Telephone

Yaacov Apelbaum-Playing Telephone

To those not familiar with the game of Telephone (also known as “Chinese Whispers” or “Russian Scandal”), it involves transferring a message amongst several individuals. The first in line whispers something to the person next to him who in turn passes the message on to the next person and so on. The game ends when the last person in line repeats the message out load. The entertainment value comes in when corruptions are introduced to the original message as it makes its way down stream. If you are wondering how playing Telephone is relevant to software development, read on.

One of the biggest challenges of developing software for new business models is that you only get a short window of opportunity to launch before your competitors join the race. This is contrary to an evolutionary product development where we use a clearly defined road map and feature list. During the early stages of new product design, no one really knows exactly what is needed or how to go about building it (that’s where the cone of uncertainty is the largest). At this early phase, the business has not yet fleshed out their model, the customer base, distribution, and pricing and the development team hasn’t yet picked the tools, platforms or frameworks.

Because of this, the design process defies common sense. If you follow the tried and true PM and SDLC practices (i.e. detailed requirements gathering and formalization phases), you are guaranteed to end up with an outdated design that does not reflect the real business needs. This can be a bit unsettling, when considering that the business requirements document you’ve worked on so hard become obsolete as soon as you send it to the printer.

Yaacov Apelbaum-PDLC Poster

The causes of this paradox stem from the difficulty in articulating and converting new business requirements to a functional product.  Just like in the game of Telephone, the original message tends to get comically distorted as it traverses the line so do the business requirements and the final software solution. Playing telephone can be fun, but it’s not if you are trying to accurately convert business requirements to a software solution in a timely and cost efficient manner.  

How do we address these challenges?  Luckily, over the years several effective methods have been devised. The following are my six favorite cures for playing software requirements telephone:

  1. Shorten the feedback loop for all development activities and streamline your design. Develop less functionality and deliver it early. This approach is based on the OODA Loop originally developed by John Boyd. One of its key requirements is that all stakeholders meet frequently (daily) and promptly iron out all newly discovered problems.
  2. Leverage RAD methods to quickly convert raw business requirements to proof of concepts and prototypes that closely resemble the real business objectives, especially as they apply to the product’s functionality and look and feel. 
  3. Keep project deliverables and key milestones to a weekly time-frame. This will help prevent problems from popping up a month or more later or going undetected until the product is in its downstream phases.
  4. Utilize SCRUM methods to quickly convert business requirements to functional features and smoke test and build daily, so at the end of the weekly cycle you will always have functional code.
  5. Put in place an effective collaborative infrastructure (document depositories, real-time progress dashboards, build automation, accurate bug tracking, etc.). There is no better way to secure trust and cooperation with the business than to maintain transparency. 
  6. Involve all team members in the decision making process, especially the architects and developers actually responsible for designing and coding the product.  Immediately bubble upwards bad news regarding show stoppers like instability, scalability, system performance, and security.

Another important factor to consider is that unfortunate truth that many development team members consider the horse trading side of software development to be tiresome. Naturally they’d rather spend their productive time coding.

Unfortunately, this isolationist mentality only exacerbates the Telephone game. As a development manager, you should work diligently to eliminate it and bring the business as close as possible to the technology. Instead of relying on outside resources to explain a business case or strategy to your team, have your team develop domain expertise in every aspect of your business, including sales, marketing, customer boarding and support. 

I have found the following two approaches to be most effective in achieving this:

  1. If your company has a sales training academy (typically a several day orientation for new salespeople), have your team members attend it. Learning the business from the point of view of a sales rep will help you design more effective and streamlined products.
  2. Actively participate in business strategy meetings and regularly brief your team on upcoming business ventures. Provide technology leadership beyond the current ongoing projects by volunteering to present new technology trends to your business units and show how these trends can benefit the business. Be proactive in putting forward new solutions that can make the business more agile and improve sales and revenue. 

Learning and mastering your company’s business model and gaining a thorough understanding of the market forces where your product will live and compete will go a long way towards being able to navigate the high seas of ambiguous requirements. It will also help you build better and financially successful software.

© Copyright 2009 Yaacov Apelbaum All Rights Reserved.

What’s with the Chicken?

Yaacov Apelbaum-Chicken

Over the past 50 years, quality and optimization have bloomed into a mature set of practices that are now leveraged in almost every industrial and manufacturing environment. Six Sigma and TQM are just two good examples for such practices. In manufacturing, unlike software development, the relationships between project schedule, component quality, and order of assembly are clearly defined and optimized (i.e. unnecessary motion is reduced.).

For a team working on an automotive assembly line, the practice of stretching deliverables to the extent possible (so long as the assembly line continues to move) is legitimate. Unfortunately, this is not the case in software development.

As product development evolved from mechanical devices to complex software packages, some previously benign scheduling habits have become vices. One such transgression is the practices of the “Schedule Chicken”. And no, this does not refer to the cafeteria’s daily practice of serving charred poultry. The origin of the term comes from 1950’s and has been documented in the movie Rebel Without a Cause. In it, two drivers are racing their hot-rods and are supposed to jump out just before their cars go over the cliff.

The first one to jump out of the car is labeled a “chicken” while the one closest to the cliff’s edge wins bragging rights. From the psychological point of view, it is interesting to note, that the rational for the “Schedule Chicken” behavior is related to the Hawk-Dove method of conflict used by players in game theory. The software development equivalent of this occurs when two or more areas of a product team claim they can deliver their features at an unrealistically early date because each assumes the other team is stretching the predictions even more then they are.

This pretense continually moves forward past one project checkpoint to the next until just before the functionality is actually due. The more mature “Schedule Chicken” practitioner will often delay addressing the obvious for as long as possible, hoping that someone else will hit the breaks and stop their car first. The ceremony in which the team lead finally admits that he can’t deliver the features on time climaxes in a primeval ritual that is reminiscent of ancient Aztec human sacrifices, albeit it’s somewhat more painful.

In the end, it is very difficult for the failed team to recover from being behind schedule, because once the truth is out, all other teams will blame them for the delay. Even if they actually do end up beating the deadline (which is highly unlikely), it will be forever impossible for them to remove the associated stigma of having been “chickens”

So how do you combat the chicken? Here are my top indications that your project has possibly been infected with an avian flu:

  1. While discussing the production schedule people use terms like guesstimating and SWAG
  2. The proposed schedule is not based on a documented matrix of previously developed products
  3. In private conversations, cross team members challenge the validity of their own schedules
  4. The team hopes to make up for lost time by adopting silver bullet tools or technologies
  5. The amount of code necessary and it’s complexity do not match available development resources
  6. Failed smoke tests, quality of builds, and bug bounces do not match the published  status reports
  7. The team is opting to build low level functionality rather than buying it and is insisting that this will actually help accelerate their delivery
  8. Development teams are regularly burning the midnight oil, are putting an exceptional amount of overtime and appear to be worn-out
  9. Project stake holders are starting to use e-mail for the creation of a “legal trail”
  10. Team leads are showing compartmentalized understanding of the project and are unable to walk through all of its dependencies (for example, they may be insisting that it’s not necessary for them to thoroughly understand other team’s implementation details or how the entire solution is going to work)
  11. Project leadership continues to commit itself to unrealistic deliverables like “zero critical bugs” or significant performance improvements, despite having priority bugs and stability issues.

The practice of Schedule Chicken may seem harmless and in some cases may even be encouraged because it promotes survivalist behavior, but in fact, if left unchecked it can have long term devastating psychological effects on the entire team and its ability to develop efficiently.

From my experience, the best mechanisms for combating the chicken are to demand open and honest communications, insisting on peer reviews, and maintaining deep visibility into all project activities.

This is not a simple task as it may goes against the grain of the entire organization but in the end, it beats the alternatives of failure and the blame game.

© Copyright 2010 Yaacov Apelbaum All Rights Reserved.

The Überteam

The Uber Team

It’s tempting to view all development teams as homogeneous masses that operate with the same efficiency. When looking for ways to improve productivity, we tend to overlook the human factor and rush to either embrace new management methodologies or focus on incrementally improving some existing processes like requirements management or bug tracking.

Certainly, there is always room for process optimization, but ultimately, these enhancements will only translate to small gains in the team’s overall performance.

In order to realize an order of magnitude improvement, we need to focus on the ‘individual’, who in my opinion is the cornerstone of the software and hardware development process. It has been shown in numerous studies such as Steve McConnell’s Rapid Development that different teams within the same organization can have significantly varying delivery efficiencies.

So how do we explain the formation of islands of excellence within the same organization? After all, don’t all teams within the company share the same HR resources, training, tools and culture?

The short answer is that excellence in operational has a lot to do with management style, team structure, and makeup.

Building and managing strong development teams goes beyond the simple management of developers to quarterly objectives and delivering functional products. What sets apart a high performance team from the garden variety one is that in addition to delivering acceptable products, the high performance team tends to exhibit more loyalty and less splintering. It works smarter, it is more creative, more cohesive and its members are more independent. A high performance team has certain qualities and a distinct spirit which emanates from the individuals in the team.

A high performance team’s esprit de corps is derived from a strong sense of individual belonging, common culture and shared vision. Building and managing this type of a team requires a clear definition of the team’s identity, documented history of success (and failure!), the cultivation of elitism (i.e. an aggressive selection process which makes team membership a coveted privilege), stoking the competitive fire (identifying the competition and targeting it), striving to being different than others and having a reputation for taking on and delivering difficult assignments. Above all, it should be a fun and intellectually rewarding environment.

Even though high performance teams are independent of project type and technology (they are certainly not limited to early stage startups), they all share certain characteristics. I call these the Seven Golden Habits, they are:

  1. Preach and Practice Honesty and Integrity – Always insist that all team dealings and communications (internal and external) be honest and open. The primary building block of all human relationships and the cornerstone of good leadership is integrity (both professional and personal), so you must show personal integrity (in moral decisions always follow the “Et si omnes ego non” principal) and instill its importance in your directs and their team members. You can always explain incompetence and overcome it. Not so with dishonesty and poor integrity.
  2. Promote High Technical, Operational, and Personal Standards – Place strong emphasis on recruiting bright and talented individuals who are passionate about their work and are domain experts. Demand that each team member provides leadership in one or more areas. Work with each team member to develop customized training and professional growth plans and make sure that each has a clearly defined career path that goes beyond basic corporate training (plans for an advanced degree, leadership opportunities, professional certifications, etc.). Inspire them to continually learn so as to further excel. Organize training in emerging technologies, standards and best practices. Promote industry visibility for team members by encouraging them to present papers at conferences and to publish. Public visibility for individual members = visibility for you and the entire team.
  3. Emphasize Accountability and Responsibility – Carefully delegate responsibility and provide your directs and team members with ample opportunities for exposure and high visibility success. Never micro manage! At the same time, expect all assignments to be completed promptly and with a high level of quality. Do not tolerate procrastination and repeated failure to deliver projects on time, over budget or with inferior quality. Provide talented team members with frequent leadership opportunities. Generously give credit where it is due and swiftly sanction habitual underperformers.
  4. Promote Cultural Diversity, Sociability and Friendliness – Recruit heterogeneous individuals (promote cultural, racial and gender diversity). Consider the stimulating exchange of ideas between team members critical for team success as well as for your own personal growth as a manager. As such, encourage the establishment of forums and strategies to build strong collaborative teamwork, some of which may include: inner team mentoring and tutoring, brown bag technical luncheons and sponsorship of after work activities. Do not tolerate any abusive behavior within your team. Get to know each team member personally. Learn about their hobbies, their family and their personal concerns.
  5. Stop the Buck and Delegate – Serve as a single executive focal-point for your management and directs (handling both inbound and outbound escalations). Build, motivate and promote inner team leadership and a successful culture that is based on mutual benefits. Empower direct reports to contribute to the development strategy, while insuring that their action plans and implementations are based on the official strategic goals and well defined market opportunities.
  6. Develop a Competitive and Rewarding Workplace – Institute spot cash bonus programs and prizes (gift cards are a favorite), permit a certain amount of telecommuting when possible, subsidize technical books, on-line education, and hardware and software for personal use, place strong emphasis on social bonding (including after work hours activities and membership in online social networks).
  7. Deal with Ambiguity Effectively – Due to their intangible nature, ambiguity and uncertainty can have devastating effects on any development team. Ambiguity tends to increase with project size and complexity and can stem from many sources that may not be under your team’s control (i.e. budget, schedule, requirements, technology, strategy, etc,). You should expect ambiguity as a given and develop approaches to elevate and counter its effect on your team’s ability to shift gears quickly without having all the facts.

Some of the coping techniques I have used to deal with ambiguity include:

  • Avoid “analysis paralysis” and “deadlocks” – Even though most of us subscribe to a structured, orderly and predictable management methodology, in the absence of more information, you should feel comfortable relying on your team’s advice as well as your personal experience and intuition to reach a decision without endlessly pondering all of the options.
  • Keep clear from the tried-and-true trap – Under conditions where the work threatened by various conditions of uncertainty, traditional solutions may not be feasible, you should push the team to innovate, think out the box, and take calculated risks to insure that business objectives are met.
  • Manage the effects of ambiguity on the team – Understanding the relationship between anxiety, impatience and uncertainty, you should work to develop an effective communication plan and control the effects of uncertainty on the team by: providing daily communication (meetings, e-mails, briefings, etc.), fighting rumors, requiring every team member to provide constructive suggestions, eliminating team splintering and factioning, speaking externally with a single voice, and avoiding finger pointing and blame.

These rules work well whether you are first forming a new team or transitioning forward a mature (and perhaps stubborn) one. Whatever the constellation of your corporate environment and culture is, these are tried and true methods that will yield significant improvements in operational efficiency and product quality.

Contrary to the old adage, you certainly can (and should!) teach an old pooch new tricks.

Good luck!

© Copyright 2008 Yaacov Apelbaum All Rights Reserved.