Friday 28 September 2007

Installing Jira - The Development Support Tool

We are a relatively small (less than 20 employees) software development house with about half a dozen mainline products as well as any number of consulting projects going on. We have some very big name clients in several countries and therefore need to ensure work is completed on time and efficiently.

While there are normal enhancement projects and major installation projects going on, there are also a number of items that come up for customers wanting changes or where something doesn't go quite as planned. These can disrupt the programmers day and disturb the development of future enhancements or otherwise frustrate the project timelines.

We have a web based system for clients to record requests and log bugs. This is monitored by our support staff and an email is fired off to developers where necessary.

All this means that as more emails come in, the developer has to judge the urgency of the email and either drop everything to perform that task, or note that this is another item that he has to do in the future. While they are very good at what they do, I was concerned for several reasons.

  1. I don't have a view on what a developer is doing at any time, or what he has done
  2. I don't have any way to tell what the workload of a particular developer is. This means I don't know to spread the load between developers when one is overloaded and another is just doing some normal housework
  3. Programmers can easily forget tasks
  4. It doesn't assist in any process to development
  5. I can't plan to ensure enough resources are available.
  6. I could go on, but you get the idea...

Generally though, it makes the whole development area invisible to me as the manager.

I knew that I needed to install a development process, but I knew immediately, I needed a tool to give me the visibility on what's being worked on work to correctly design a development process. I had used Jira before so the decision was an easy one for me. Setting it up was easy and done in minutes, but I wanted a number of changes to the permission and notification schemes, then it was a matter if creating some projects. Done.

It worked well and I installed a testing project so that people can try out the system without disturbing real work.

Its been installed now for about a week but the uptake has not been immediate. I'll have to do a demo on Monday to show everyone how it works, assure them that its not going to take over their day with mundane admin tasks, and finally show them a few neat wow bits. After that I'll be enforcing the use of it for the next month.

If, after that trial, no-one is convinced, then I'll take it away, but I'll need to know what to replace it with if that is the case. There seems to be a few main problems with installing this work request system that I'll have to overcome. These include:

  • It seems like more work for no reason. While it may take a minute or less to create a new Jira issue, there are huge benefits for the user, the company, and the client - get over it.
  • Its a way for the boss to monitor my work so he can breathe down my neck. In fact, its a way to STOP the boss breathing down your neck as he can see what's going on instead of bugging you every half hour. As for monitoring, your not that interesting that I want to spend all my time "monitoring" what you are doing "right now", I'm a lot busier than that but a quick glance to review workloads will tell me if you have too much work and need help.
  • I get lots of little emails from lots of people wanting me to do things that only take a minute or two, why should I have to put all these into Jira?. By putting these into Jira, these little jobs will not only show you how much time they take up, but you will never, ever forget to do them. I have set up an email for each project in Jira, simply forward to the email to that address and it'll create your Jira issue instantly.
  • I'm a senior developer, surely "I" don't have to do this. Yes, sadly I'm afraid that you do.

I'm sure that once everyone is using it and the advantages are being seen, then it'll resolve any arguments. To be fair, no argument have been forthcoming, but I can sense what's not being said at times. They are a great bunch or people that I enjoy working with, I just think I can make life easier for them with this tool. I'll let you know how it goes.

Friday 21 September 2007

Back again

Thanks for your patience, I have finally shifted house and started my new role as Software Development Manager for a software house in Hamilton.

The shift included a few days in Wellington visiting friends and generally spending time away. Holidays have been a very rare occurrence for me, having at one time, spent more than 12 years without a holiday, I find that taking a break occasionally is a requirement.

The new role is interesting. Based on Oracle technologies the product is a very stable and sizeable system. The team contains some excellent technical expertise and I will be working hard to install some processes and a decent methodology to ensure that they are supported as much as possible.

That's all for now as I'm still finding my feet here.

Monday 10 September 2007

Returning the outer directory

Returning the directory before the current one should be a simple one-line call but I have so often seen a number of very highly convoluted ways of finding the last slash (\) in the directory string and using copy() and other ways that I have decided to relate a simple and direct way to obtain any or all directories prior to, and including the current one.

The problem with finding the last slash is that you have to watch out for cases where the current directory is a root directory. The good news is that you can ignore all of that.

You should know that finding the currently running exe program name including directories is Application.ExeName found in SysUtils. This should return something like..

C:\Program Files\CodeGear\Delphi\5.0\bin\MyProgram.exe

Of course the directory structure may be different as I'm sure you're not running your programs in the Delphi \bin directory :o)

But to get the directory that MyProgram.exe is running in, the call is ExtractFileDir(Application.ExeName). This returns..

C:\Program Files\CodeGear\Delphi\5.0\bin

ExtractFilePath() dies something similar. It simply returns the string up to the last delimiter (the slash). In other words, anything past the last delimiter is ignored as it is assumed to be the program name. Excellent, this means that we can use that fact on the above directory structure to return the previous directory.

PreviousDir := ExtractFilePath(ExtractFileDir(Application.ExeName));

This should return a directory structure of
C:\Program Files\CodeGear\Delphi\5.0\

We can continue to use ExtractFileDir() to return each directory in the tree until we reach the root directory (perhaps checking if PreviousDir = LastDir?). For example:

Dir := ExtractFilePath(ExtractFileDir(ExtractFileDir(Application.ExeName)));

Should return one directory up from the previous dir, or in this case:
C:\Program Files\CodeGear\Delphi\

So there you have it. A single call that returns the previous directory without having to bother with searching for the slash or worrying about the root directory issue.

Thursday 6 September 2007

Accessing Interbase/Firebird Metadata in Delphi

WARNING: Interbase and Firebird system tables are not for the faint hearted. It is not recommended that you alter anything in these tables if you ever want to use them again - and keep your job. Major stuff-ups can occur.

I was looking at some old Delphi 7 code of mine when I was attempting to get in behind the scenes of Interbase and Firebird tables and fields to learn a little more about them. I came across these little pieces of information I'd like to share.

The following will list all the tables in one grid and, for each table, list all the fields in another grid. Drop on your TIBDatabase and direct it at your Interbase or Firebird server.

Now drop a TIBTransaction, TIBQuery, TDataSource, and a TDBGrid and connect them all.

Enter the following query into the IBQuery1...

  SELECT DISTINCT RDB$RELATION_NAME as MyTable
FROM RDB$RELATION_FIELDS
WHERE RDB$SYSTEM_FLAG=0
AND RDB$VIEW_CONTEXT IS NULL
ORDER BY RDB$RELATION_NAME


and make IBQuery1 live. Now drop on another TDBGrid, TIBQuery and TDataSource and connect them. This time, select IBQuery2 and add TDataSource1 in the "DataSource" Property. This will ensure that the second query will look to the first query to fill in the parameters. That parameter will be the field MyTable.

Enter the following query string into the SQL property of IBQuery2...

  SELECT RDB$FIELD_NAME AS FIELDS
FROM RDB$RELATION_FIELDS
WHERE RDB$RELATION_NAME = :MyTable
ORDER BY RDB$FIELD_POSITION


and make that query live. When you run that program, selecting tables in the first grid will show all the field information for that table in the second grid.

So far I have the following types in the field RDB$FIELD_TYPE. These can translate to...


  • 8 = Integer
  • 10 = Float
  • 12 = Date
  • 13 = Time
  • 14 = Char
  • 35 = TimeStamp
  • 37 = VarChar
  • 261 = Blob


There'll be a definitive list somewhere but that's all I have needed so far. I got that list from the internet somewhere but it was so long ago that I have forgotten where (thanks to whoever that was).

That will get you started, but here are some other queries that will return more metadata information...

Return indexes for a table

  SELECT RDB$INDEX_NAME 
FROM RDB$INDICES
WHERE RDB$RELATION_NAME = :MyTable
AND RDB$UNIQUE_FLAG IS NULL
AND RDB$FOREIGN_KEY IS NULL


Return all Generators

  SELECT RDB$GENERATOR_NAME 
FROM RDB$GENERATORS
WHERE RDB$SYSTEM_FLAG IS NULL


Return all Triggers

  SELECT * FROM 
RDB$TRIGGERS
WHERE RDB$SYSTEM_FLAG IS NULL


The shifters are coming in the morning so I'll post this now, please excuse any typing errors. Enjoy your day.

Remember: A positive attitude may not solve all your problems, but it will annoy enough people to make it worth the effort. Herm Albright (1876 - 1944)

Wednesday 5 September 2007

Scrolling to the right place in TMemo

After programmatically inserting a number of lines I find that the TMemo will show at the last line as it normally should. However, sometimes what I really want is for TMemo to move to the first line, or perhaps even move to the top of the last lot of inserted lines.

Here are a few little gems I have picked up on my travels that work a treat.

Move to a particular line in a Memo...

function MemoMoveToLine(LineNo: integer);
begin
with Memo1 do
begin
SelStart := Perform(EM_LINEINDEX, LineNo, 0);
Perform(EM_SCROLLCARET, LinePos, 0);
end;
end;


What this is doing is calling TControl.Perform. This function actually uses a Windows message to perform some task, in this case placing the cursor at a line number, and then scrolling to that line. Let's take a look at using that function...

var
CurrentLine: integer;
begin
// Get the current position
with Memo1 do
CurrentLine := Perform(EM_LINEFROMCHAR, SelStart, 0);
AddLotsOfLines;
// Move to the line we started at
MemoMoveToLine(CurrentLine);
// Now Move to the end
MemoMoveToLine(Memo1.Lines.Count);
// Now Move to the start
MemoMoveToLine(0);
end;


And there you have it.

Ramblings


I'm well into packing in the evenings now and surrounded by boxes. The truck comes in 2 days to take my furniture to Hamilton where I will start work as Software Development Manager about mid month. I look forward to the challange of a new team and new company.

Although it will be hard to leave the Mount and this wonderful job and environment, my family is still at the Mount so I'll be back often. Its only a short drive to get here to see them and I'll probably end up with a caravan here for easy weekend stays.

I'll still be codeing in Delphi for my own programs (and sanity), always have, always will. This will ensure that the blog stays relative to Delphi, although I might introduce a few new subjects as well.