Thursday, August 20, 2009

Focus on Usability: Part III of What’s Software Development All About?

This is the data entry form for an Access 2.0 database containing the Novell 3.x and 4.0 servers for Key Services Corporation and their corresponding parent organizations and administrators which I created in 1994 and maintained until 1997. In the time this was considered a great improvement over the character based green on black mainframe data entry screens. The user could tab from field to field, use the mouse or key combinations to press the buttons, and proceed fairly rapidly from entering new data to updating or deleting existing data. This was also the way many Visual Basic 3.0 to 6.0 application forms looked in the mid to late 90s.

Then the Internet came on full steam, and web usability became more of an issue. Many sites continued, and some still do, to use web input pages that look like these old Windows 3.x forms. They demonstrated these problems:

  • Users were forced to scroll right many times to view and fill in any input fields to the right. If these fields off to the right were required fields, it was usually harder to find them if the page failed to submit.
  • Many times buttons were used instead of links to open up new browser windows (which is an outmoded practice in itself) or to navigate to a new page.
  • Unrelated data fields were often on the same page, and sometimes not even grouped separately.
  • Pages which had a lot of data entry fields would take longer to post the data to the server.
  • The pages often did not have an apparent logical grouping or flow to the input fields and labels.

I believe that one of the reasons why pages designed according to this outmoded idiom persist and continue on some sites is that these pages are designed by business analysts who have not been trained in contemporary web design. I’ve seen this enough to believe that it’s not a problem restricted to one corporation.

Here is a more common way in which data entry forms are done on contemporary web pages. It’s so common that users practically do not have to think when they see a form like this. I’ve worked with web pages that follow this general pattern since 2000, in classic ASP, and it’s a more common and usable idiom on the web.

First Name First Name Text Box
Middle Initial Middle Initial Text Box
Last Name Last Name Text Box
Address 1 Address 1 Text Box
Address 2 Address 2 Text Box
City City Text Box
State State Dropdown List Box
Zip Zip Text Box
Submit

Here are some general guidelines for business analysts prototyping data entry forms/pages:

  • Understand whether the data entry forms/pages are to be done in Windows or Web.
  • Windows data entry pages should be prototyped with the print orientation set to landscape if the prototypes are done in Word or Powerpoint.
  • Web input forms need to be prototyped with the print orientation set to portrait if the prototypes are done in Word or Powerpoint.
  • Learn which input controls are appropriate to the type of input (checkboxes, radio buttons, list boxes, and dropdown list boxes).
  • Document the correct text tooltips as well as field labels. Both web pages and Windows forms can implement tooltips, and they form a very handy way to provide immediate help to the user.
  • Go over the prototypes with an architect or senior developer.
  • Avoid complexity in one form or page; seek to have one form or page handle input and updating of one set of data and have it do that well.
  • Be flexible and cooperative with developers if they suggest changes to the prototypes when they are actually implemented. The developers are the first users of the prototypes, and they can often make very constructive suggestions.
  • Consider getting and reading the book Paper Prototyping by Carolyn Snyder, and going through a paper prototyping process to investigate usability before any kind of implementation is started.

Saturday, August 8, 2009

Downtime Pursuits

In IT there can be some times when a developer doesn’t have much to do. For a consultant, this is often during the ramp-up time for a project, when the project is first starting, or during the cool down phase, when the project has been elevated to production, and it’s mostly production bug fixes for a while. When I’ve been an in-house developer, this has usually been times between projects.

I’m persuaded that the wise use of this time can make a huge difference for one’s own career progress and the good of the department and corporation as a whole. Unfortunately, there’s often a dearth of good ideas on what to do during this time, and seemingly irrational pushback if someone tries to do something which does not originate in the mind of a business sponsor who is an upper level manager. I think that the pushback comes at least partially from an unarticulated question, “Where we will charge the hours for this idea?” And I think that otherwise pushback comes from the unarticulated question, “Who will get credit for this?” And finally, I think that pushback comes from managers and supervisors who seem to think, “I don’t have time to supervise this or the political capital to get this done.”

Here are the five basic uses to which this time can be put:

  • Process improvements
  • Project or departmental infrastructure projects
  • Documentation
  • Personal R & D time
  • Peer training and assistance.

In short, these are the five things for which there are common complaints that there is no time for these things to be done when a project is actually underway, and managers, project managers and team leads are worried about making a due date and adhering to a project timeline. The truth is that these kinds of activities can actually reduce project timelines if they are chosen wisely. They come under ‘working ahead’ and ‘working smart' activities, rather than frenetic ‘I’m working as hard as I can’ activities. And if anyone is worried about charging time for these activities, someone needs to make sure that there is simply a bucket to charge these activities.

The key is that unlike a project for a high level business sponsor, these activities can be:

  • Low priority: Production issues or business projects take priority over completion of these projects.
  • IT centered: Directed to the greater efficiency of IT (developers, testers, business analysts, system administrators).
  • Automate minor manual business processes (1-2 users) such as repetitive data entry, credit inquiries or faxes of incoming information.
  • Left temporarily incomplete to be taken up by others to complete when time and availability changes.

Here are some ideas that I have for these kinds of activities.

  • Developing Visual Studio template projects for the kinds of projects which are anticipated in the near future.
  • Implementing Sharepoint sites with MOSS 2007 or WSS 3.0 for development teams or projects.
  • Developing code generation utilities for future projects.
  • Developing an automated build system with CruiseControl.NET.
  • Developing other departmental or IT utilities.
  • Developing Visual Studio add-ins or Guidance Automation recipes for future projects.
  • Documenting existing systems.
  • Updating existing documentation for existing systems (lots of existing documentation contains omissions and errors, and degrades over time).
  • Serving as a scribe to take meeting minutes to make sure that decisions and action items are clearly documented (lots of meetings end with no one agreeing on what was actually decided and who would implement the decisions).
  • Internal cross training.
  • Performance monitoring existing systems (lots of existing production systems, particularly those with a  mid to small number of users, never receive any monitoring once in production).
  • Looking through event logs for avoidable and fixable errors (lots of fixable problems never are found and fixed even if sufficient information is logged).
  • Cleaning up bloated error logs and event logs.
  • Evaluating and upgrading existing error logging functionality.
  • Reviewing existing code for possible improvements.
  • Reviewing existing databases for possible improvements (lots of existing databases are never evaluated after being put into production).
  • Review of existing systems and databases for security bugs and user access (lots of existing systems and databases retain access rights for transferred or terminated employees or too high access levels for non-administrators).
  • Developing archiving solutions for existing databases (lots of existing databases never archive outmoded or obsolete information).
  • Developing ETL solutions for existing databases.
  • Developing automated unit tests for existing code modules (lots of existing code modules never get regression unit tests developed for them, and may break when enhancements are added).
  • Cleaning up source control projects and archiving outmoded code and projects.
  • Preparing and delivering peer training presentations, such as for a Lunch ‘n Learn or departmental meetings.
  • Conducting research on new technologies which could prove useful.
  • Developing prototypes for new technologies which could prove useful.
  • Automation of minor business processes with VSTO, VSTA and Microsoft Office.
  • Development of a process improvement suggestion site.
  • Development of a departmental wiki.

In short, the tunnel vision that sees any useful activity as having to be initiated by a person who is an IT or business unit manager can miss out on many possible improvements which can be pursued in the time when developers are not dedicated either to production support fixes or major projects. And yet these minor improvements add up over time and add to the bottom line value and efficiency of an IT organization and the corporation as a whole.

Here are some ideas to help this take place in a department or IT organization.

  • Encourage and reward the initiative and courage it takes to envision and suggest process improvements and to make wise use of down time.
  • Serve as an ‘angel’s advocate’ for new ideas and process improvements rather than as a cynical devil’s advocate who commits infanticide on the ideas of others.
  • Allow developers to get close enough to end users to develop the kind of trust to where end users can share their process improvement ideas.
  • Allow developers to see end users at work enough to where they can spot opportunities for process improvements.
  • Allow developers to explore new technologies early enough to find possible implementations.
  • Make sure that ‘idea theft’ is not rewarded either in the short term or long term.

Thursday, August 6, 2009

Blast from the Past: Automating Processes with Access Basic Code

A long time, in a galaxy far, far away, I developed and maintained an Access database of Novell servers for the KeyCorp Help Desk. I developed the following code Microsoft Access Basic to take care of the chore of updating the LAN database when a new Novell server came onto the corporate Wide Area Network (WAN). Another example of the way we used to do things! Note the date in the header!

Sub Updatewanservers ()

'***********************************************************************

' Author: Dale Hawthorne

' Date: September 3, 1996

'***********************************************************************

' Purpose: to run the batch file for the NLIST command, read the resulting

' text file into a temporary table, and then perform a comparison query

' with the LAN table that will identify the new servers for research and

' additional input

'************************************************************************

Dim strShellCommand As String

Dim varVerification As Variant

Dim intFileNum As Integer

Dim strInputChar As String

Dim i As Integer

Dim strNLine As String

Dim strServer As String * 25

Dim strOutputChar As String

Dim strStatusBar As String

Dim strSQLStatement As String

Dim strTitleText As String

Dim strMessageText As String

Dim dbCurrent As Database

Dim qryUpdateLANTable As QueryDef

Dim rstUpdateSet As Recordset

'Opening initialization of variables

On Error GoTo UpdateError

strNLine = Chr$(13) & Chr$(10)

Set dbCurrent = DBEngine.Workspaces(0).Databases(0)

'This section checks for the existence of previous files prior to running

'this routine

Application.Echo False

DoCmd Hourglass True

DoCmd SetWarnings False

varVerification = Dir$("C:\SERVER.BAT")

If varVerification <> "" Then

Kill "C:\SERVER.BAT"

End If

varVerification = Dir$("C:\SERVERS.TXT")

If varVerification <> "" Then

Kill "C:\SERVERS.TXT"

End If

If IsTableQuery("", "tblTempServers") Then

strSQLStatement = "DROP TABLE [tblTempServers];"

dbCurrent.Execute (strSQLStatement)

End If

'This command toggles the flags on the New field

strStatusBar = "Now preparing LAN Table for input . . ."

varVerification = SysCmd(SYSCMD_SETSTATUS, strStatusBar)

strSQLStatement = "UPDATE DISTINCTROW LAN SET LAN.New = ""No"";"

dbCurrent.Execute (strSQLStatement)

'This section creates a text file from the NLIST command

strShellCommand = "@ECHO OFF" & strNLine & "NLIST server /a/b > c:\servers.txt"

intFileNum = FreeFile

Open "C:\SERVER.BAT" For Binary As intFileNum

For i = 1 To Len(strShellCommand)

strInputChar = Mid(strShellCommand, i, 1)

Put intFileNum, , strInputChar

Next

Close intFileNum

varVerification = SysCmd(SYSCMD_CLEARSTATUS, " ")

'This warns the user of the batch file about to run

strMessageText = "A batch file will now run to list all active servers."

strMessageText = strMessageText & strNLine & "The screen will be black "

strMessageText = strMessageText & "for several minutes while this happens."

strTitleText = "Warning!"

MsgBox strMessageText, , strTitleText

'This runs the batch file

varVerification = Shell("C:\SERVER.BAT")

'now this waits until the batch file has ceased execution

If varVerification >= 32 Then

Do While GetModuleUsage(varVerification)

DoEvents

Loop

End If

' This section opens the text file for input into an Access Table

varVerification = SysCmd(SYSCMD_CLEARSTATUS, "Now retrieving values into Access .. ")

varVerification = SysCmd(SYSCMD_REMOVEMETER)

strSQLStatement = "CREATE TABLE [tblTempServers] ([Server] TEXT);"

dbCurrent.Execute (strSQLStatement)

intFileNum = FreeFile

Open "C:\SERVERS.TXT" For Binary As intFileNum

Seek intFileNum, 425

Do Until EOF(intFileNum)

strInputChar = " "

Do Until strInputChar = Chr(10)

Get intFileNum, , strInputChar

Loop

Get intFileNum, Seek(intFileNum) + 1, strServer

strServer = Trim(strServer)

If InStr(1, strServer, "A total") = 0 Then

strSQLStatement = "INSERT INTO tblTempServers ([Server])" & strNLine

strSQLStatement = strSQLStatement & "VALUES (""" & strServer & """);"

dbCurrent.Execute (strSQLStatement)

End If

Loop

Close intFileNum

dbCurrent.Execute ("qryUpdateFromWan")

Kill "C:\SERVER.BAT,C:\SERVERS.TXT"

strSQLStatement = "DROP TABLE [tblTempServers];"

dbCurrent.Execute (strSQLStatement)

Application.Echo True

DoCmd Hourglass False

DoCmd SetWarnings True

Exit Sub

UpdateError:

If Err = 3075 Or 375 Then

Resume Next

Else

MsgBox "Error" & Err & ": " & Error$

Close intFileNum

Application.Echo True

DoCmd Hourglass False

DoCmd SetWarnings True

End If

End Sub

Blast from the Past: Disconnected ADO Recordset in C++

I found this code today also. It’s done in Visual C++ 6.0, Active Template Library/COM, and it returns a disconnected ADO recordset for use in an ASP page. It shows the way things used to be done for developers that worked with web pages but didn’t like Visual Basic 6.0.

STDMETHODIMP CRequestSecurity::GetRequestSecurity(long ReqID, IDispatch ** rsetptr)

{

HRESULT hr = S_OK;

VARIANT var;

try{

_CommandPtr spCmd(__uuidof(Command));

_RecordsetPtr rs(__uuidof(Recordset));

spConn.CreateInstance(__uuidof(Connection));

hr = spConn->Open(connstr, "", "", adConnectUnspecified);

if(FAILED(hr))

_com_issue_error(hr);

spCmd->ActiveConnection = spConn;

spCmd->CommandText = "get_req_security";

spCmd->CommandType = adCmdStoredProc;

_ParameterPtr reqidprm = spCmd->CreateParameter(_bstr_t("req_id"), adInteger, adParamInput, vtMissing, _variant_t(ReqID));

spCmd->Parameters->Append(reqidprm);

rs->CursorLocation = adUseClient;

rs->Open(spCmd.GetInterfacePtr(), vtMissing, adOpenForwardOnly, adLockBatchOptimistic, -1);

if(rs->State == adStateOpen && rs->adoEOF == VARIANT_FALSE)

{

VariantInit(&var);

var.vt = VT_DISPATCH;

var.pdispVal = NULL;

rs->PutActiveConnection(var);

spConn->Close();

VariantClear(&var);

hr = rs->QueryInterface(IID_IDispatch, reinterpret_cast<void**>(rsetptr));

if(FAILED(hr))

_com_issue_error(hr);

}

else

{

hr = rs->Close();

if(FAILED(hr))

_com_issue_error(hr);

}

}

catch(_com_error &e)

{

hr = e.Error();

Error(e.ErrorMessage(), GUID_NULL, hr);

}

catch(...)

{

hr = E_FAIL;

Error(L"An unspecified error occured.", GUID_NULL, hr);

}

return hr;

}

Blast from the Past: Shutdown Utility

Today I came across some code which I had done a few years ago in Visual C++ .NET 2002 (pre-CLI). It calls the Windows API to shut down the current workstation, and it can also be used to shut down a remote workstation where the access rights exist. The .NET API does not have this capability right now. This code was part of an automated test system which needed to be able to shut down the remote workstation if there were problems with the automated test proceeding. The remote workstations were Windows 95 PCs on which .NET 1.0 ran fairly easily. If anyone needed this functionality today, this code could probably be updated to C++ CLI standards fairly easily. But, young Jedi, be sure that this power is used for good and not for evil!

Header file – the contents of SystemUtil.h:

#using <mscorlib.dll>

#define _WIN32_WINNT 0x0500

#include <windows.h>

#include <winuser.h>

#pragma once

using namespace System;

using namespace System::Runtime::InteropServices;

#pragma warning(disable : 4101)

namespace SystemUtil

{

public __gc class CSystemShutdown

{

public:

bool RemoteShutdown(String * WorkstationName, String * EventLogMessage);

bool LocalShutdown(void);

};

}

SystemUtil.cpp:

bool SystemUtil::CSystemShutdown::RemoteShutdown(String * WorkstationName, String * EventLogMessage)

{

bool rv = false;

DWORD error_code = ERROR_SUCCESS;

HANDLE hToken;

TOKEN_PRIVILEGES tkp;

String * error_string = S"Windows Error #";

if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))

{

error_code = GetLastError();

throw new Exception(error_string->Concat(error_code.ToString()));

}

else

{

::LookupPrivilegeValue((LPCTSTR)Marshal::StringToCoTaskMemAnsi(WorkstationName).ToPointer(), SE_REMOTE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1 ;

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if((::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0) && (error_code = ::GetLastError()) == ERROR_SUCCESS))

{

if(!InitiateSystemShutdownEx((LPTSTR)Marshal::StringToCoTaskMemAnsi(WorkstationName).ToPointer(),

(LPTSTR)Marshal::StringToCoTaskMemAnsi(EventLogMessage).ToPointer(),

0,

TRUE,

TRUE,

SHTDN_REASON_MAJOR_SOFTWARE))

{

error_string->Concat(error_code.ToString());

if(error_code == ERROR_ACCESS_DENIED)

{

error_string->Concat(S" Administrative rights are not in effect for remote rebooting of this workstation.");

}

throw new Exception(error_string);

}

}

else

{

throw new Exception(error_string->Concat(error_code.ToString()));

}

}

return rv;

}

bool SystemUtil::CSystemShutdown::LocalShutdown(void)

{

bool rv = false;

HANDLE hToken;

TOKEN_PRIVILEGES tkp;

DWORD error_code = ERROR_SUCCESS;

String * error_string = S"Windows Error #";

if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))

{

error_code = GetLastError();

throw new Exception(error_string->Concat(error_code.ToString()));

}

else

{

::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);

tkp.PrivilegeCount = 1 ;

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);

if((error_code = ::GetLastError()) == ERROR_SUCCESS)

{

if(!ExitWindowsEx(EWX_REBOOT | EWX_FORCEIFHUNG, 0))

{

error_code = GetLastError();

error_string->Concat(error_code.ToString());

if(error_code == ERROR_ACCESS_DENIED)

{

error_string->Concat(S" Administrative rights are not in effect for rebooting of this workstation.");

}

throw new Exception(error_string);

}

}

else

{

throw new Exception(error_string->Concat(error_code.ToString()));

}

}

return rv;

}

Technorati Tags: ,,,

Tuesday, July 21, 2009

Death Marches and Lost Patrols

Recently, while moving around some boxes of books, I came across my copy of Ed Yourdon’s Death March. This morning I read it through again, to see if there were any lessons and comparisons I could make with it since the last time that I read it four years ago.

I’ve been on three projects over the years that could have been legitimately called, ‘Death Marches.’ My supervisor called one as such, another turned into one because of false expectations and schedule pressure, and the last one had Death March features although I asked for and received assurances that it was not going to be a Death March. The human cost and constant failure of these projects forever soured me on these kinds of projects.

With the recent adoption of Agile methodologies and even more use of the Agile buzzword for a spiral project lifecycle, more projects seem to be turning into ‘Lost Patrols’ rather than Death Marches. The metaphor of the ‘Lost Patrol’ comes from the Death March book as well. Death Marches usually have a goal and and a defined endpoint, in the delivery of a definite set of required functionality and features. Lost Patrols start out with a definite goal but end up getting lost in the forest.

This loss of the definite endpoint is a known problem with the spiral project lifecycle methodology. It can hit Agile when the project requirements and endpoints are left so undefined that the project team becomes lost. Activity may continue, but productivity does not. One of the perverse things that can happen with an Agile project is that the business owners and stakeholders get addicted to generating requirements. It becomes more interesting to them to keep on putting together list of requirements than to see a finished product.

As usual, the solution probably is to stop the project, get some bearings, and restart when project requirements and endpoints are more stable. This may require getting the business stakeholders to agree on what should be delivered when and when the product will go out of new development into maintenance and enhancement mode.

Thursday, June 25, 2009

Part II: How to Make a Consulting Gig Mutually Effective and Rewarding

Most of what I put down in my previous post, Part I: How to Make a Consulting Gig Mutually Effective and Rewarding, had to do with defining the assignment and preparing for the new consultant. Here are some more suggestions that I have to make.

Make Sure That All Prospective Interviewers Are Prepared.

  • Interviewers need to have a current copy of the consultant’s resume. When I’m interviewed about a prospective assignment, I usually have additional copies with my business card and contact information. It’s best to give any prospective interviewers a copy of the resume far enough in advance to go over and assess the fit of the consultant to the assignment.
  • Interviewers need to have a current job description for the assignment. The assignment is usually important enough and costly enough to write down a list of what is relevant to the assignment.
  • Interviewers need to know what skills and experience are relevant to the assignment. This simply comes down to what parts of the job description are the most important. Sometimes job descriptions contain ‘nice to have’ skills and experience as requirements, and requirements as ‘nice to have’.
  • Interviewers need to pursue the technical screening as determining whether the consultant has the required skills and experience but also the capabilities to do the job. This technical screening is not technical oneupmanship, or looking for a weak area on the resume, but assessing the fit to the assignment. They need to look at the intangibles as well, especially where the team will be working with new technology. They need to assess whether the consultant will work to find answers, solve problems and do what it takes to make the project succeed.
  • Interviewers need to allow for time for the consultant to ask questions and find out about the job to see whether it’s a fit from his or her perspective. 

Make Sure That the Team Is Prepared to Integrate with the New Consultant.

  • Clearly communicate that you, as a manager, expect that they will give the new consultant or contractor full professional cooperation. Sometimes experienced and highly qualified consultants may be threatening to less experienced members of the department or organization. Be on the lookout for defensive denigration (disparagement of the consultant’s credentials, capabilities and experience – which may count in some cases as slander or libel according to the legal definitions), misdirection (deliberately providing false or misleading information and direction), and stonewalling (refusal to provide necessary information and assistance to get the job done) in these cases. Understand that this kind of behavior can sink a project and undermines your ability as a manager to bring in the people that you need to get the job done.
  • Make sure that you and others understand that ‘new’ in the case of a consultant does not mean entry level. Experienced consultants often need little more than current standards and guidelines, project requirements and other project documents, a development PC, and access to workstations, servers, etc. They do not need to have team members try to guide them through the basics of how to code, etc. For myself, I try to make sure that beyond the particulars of the corporate environment and project specifications, that I know all that’s needed to fulfill my responsibilities and more.
  • Make time for communication with the consultant. As a consultant, I aim to be as ‘low maintenance’ as possible – but that does not mean ‘no maintenance’! Rather, I still need to touch base, and communicate progress, ask questions, make sure that I’m on the right track, etc. My aim in this is simply to make sure that I’m doing the right things and providing what is expected, and, preferably, more than is expected. It’s usually a good idea to have a weekly or biweekly one on one meeting with a consultant. Management by walking around and asking how things are going works well also.

Make Sure That the New Consultant Receives Realistic and Workable Expectations and Assessment.

  • Realistic and workable expectations and assessment means sufficient time to complete the assigned task. Experienced consultants can often do much in a short time, but each one of us does require time to produce whatever is being specified. For example, no matter how many years a person codes, no one writes perfect code the first time. What is checked into a source code repository first may not be the final submission but rather simply be to preserve initial attempts against possible loss. Moreover, the code that is produced still needs to be evaluated, unit tested and possibly refactored. The end result is what counts the most. For myself, I aim to produce well designed, documented, understandable and production ready code. I can usually produce this faster than less experienced programmers. But what comes out of the end of my fingers the first time won’t be perfect, although I aim for the end result to fulfill the requirements and functional as possible.
  • Realistic and workable expectations means receiving tasks in accord with the skills and experience of the consultant. For instance, I can do a number of support tasks such as installations and defining security roles and users, but I’ve never worked as a network administrator. On the other hand, I’ve worked with some very good manual and functional testers, but I do not consider myself competent to do manual system and functional testing. In fact, my total time for manual system testing over 15+ years of IT experience comes down to maybe 8 hours total. (For more on this, see Joel on Software: Top Five (Wrong) Reasons You Don't Have Testers.) It’s usually better to try to find someone who is experienced and trained in these tasks rather than try to throw a consultant experienced in other areas at these tasks.
  • Realistic and workable expectations and assessment means a method for the consultant to report on his own work. Work out some kind of regular reporting mechanism. In previous assignments I put out a weekly report of my activities where the manager expected one. I’m thinking now that this is advisable in every case, to make sure that the manager gets an accurate understanding of the productivity of the consultant.