Wednesday, 23 April 2008

Runtime Errors Suck!

I hate runtime errors. I think that functions in C/AL that can generate runtime errors should be deprecated where possible or altered in their function. Let me explain with an example.

Let's say you have two fields both called Description on two different tables. One of your tables is the Item table and the other is a new table called "New Item" (OK so I'm not going to win any prizes for imaginative table names in this example.) You want to make an assignment from one field value to another so you would do something like this:

l_NewItem.Description := l_Item.Description;

When your code runs the Description gets copied across and everyone's a happy bunny. But, let's say your customer says they want their item description to be made 20 characters bigger. What happens then?

Well let's assume that you increase the size of the Item Description field by 20 characters. Everything still works. Or so you think.

When NAV executes the line of code that previously worked fine on a record that has more characters in the new larger string than will fit in the other string, you (or more likely the first user that comes across that bit of functionality in your production system) will get a Runtime Error. Blurgghhh!

I have programmed in a few languages and C/AL is the only language I have ever come across that does this. What would other languages do? They would truncate the value and continue. They would assume, "hey, the programmer wants me to put a 50 character string in a 30 character string, he must know what he's doing, so I'll just give him as much as I can."

I like that, it's friendly, it makes me feel warm and fuzzy. Why can't C/AL do the same?

But there is a far worse function that can throw runtime errors and this should be banned altogether! TRANSFERFIELDS.

TRANSFERFIELDS is evil. It has to go.

What does it do? Well it, er, transfers, erm, fields (duh?) It is used to transfer fields from one table to another. Sounds cool doesn't it? How does it decide which fields should be copied? It uses the field ID.

What? The totally arbitrary field ID? Surely not, that would be crazy. Yup you heard it the field ID. NAV will attempt to make an assignment between two fields with the same ID, but different names and different types. And what do you think happens if the field types are incompatible?

Run Time Error.

The reason this function is evil is it lures the unsuspecting programmer into its little trap. It looks innocent. It looks like it may save you some time. Don't be lazy. Assign the fields one by one. Think about it, if you have 10 lines of code that assign each field from one table to another, anyone reading your code knows exactly what is happening. If you really want to be good, create a function on the table called CopyFromItem() or something similar. Then do the field assignments in the function so your code would look something like this:


Now isn't that better?

So what prompted me to have this little rant? Well I am doing an upgrade from v3.70 to v5.00 at the moment and the damn thing just failed with a run time error. Some of the code in the standard upgrade toolkit gave me this error message:

The two fields below must have the same type:
Field: JobTaskNo <-- Table
Table: Temp Job Task Phase Step Comb. <-- Temp Phase Step Task Doc Line
Type: Code20 <-- Integer

Can you guess what caused this error?


So if the expert coders at Microsoft get caught out, what chance do we have?


Davor said...

Great point to bring up. But I think you might be contradicting yourself a bit when you say in 1st example (text assignment) that Navision should assume programmer knows what he's doing. The result of assigning text while concatenating it will in fact cause data loss. Sure it might be trivial, but this is an ERP system and losing data is not our business ;-)
I do try to make a point of programming text assignment between fields from different tables as:

l_NewItem.Description := COPYSTR(l_Item.Description, 1, MAXSTRLEN(l_NewItem.Description);

when I'm not woried about trimming the text, in all other cases I would rather have Navision run-time error on me so I do get it brought up to my attention that something might need fixing here.

On the other hand (and getting back to the 'contradiction' remark above) you next conjure that the programmer doesn't know what he's doing if he's using TRANSFERFIELDS funcion. Well make up your mind (j/k ;-)

The fact is that TRANSFERFIELDS is a function that like any other should be used with care and in controlled scenarios, one of which first comes to mind is during posting routines when you need to copy 100+ fields from header/line table to posted header / line table (or to several "posted" tables). I would assume that's what it was invented to do mostly.
The added benefit of having TRANSFERFIELDS in posting routines is that you can easily add custom fields to posting and be sure the new fields will propagate to all of the relevant posted tables simply by creating them with the same ID in all related tables. Nice feature indeed as you say, but one that can shoot you in the foot as well.

Nice blog - keep it up!

Gaspode said...

Hi davor,

Wow thanks for taking the time to comment on my blog. I really do appreciate it. I realised after I had posted that I didn't include the COPYSTR code for getting around the run time error so thanks for saving me that little tidy up job. :-)

I agree with you that maybe it is better to get a runtime error than to lose data. But the other ERP systems I have programmed in (XAL and Axapta) didn't throw runtime errors in the same situation. I think that there are better ways of solving the problem of not losing data - maybe when we get Extended Data Types in NAV this problem will go away.

As for contradicting myself, I don't think I did. But then again, I would say that, wouldn't I? What you interpretted as "the programmer doesn't know what he is doing if he's using TRANSFERFIELDS function." is not what I was trying to say.

I was trying to say "the programmer does not know what is going to happen if he's using TRANSFERFIELDS." Hopefully you can see the difference between these two things. Why does the programmer not know what is going to happen? Well the function is dependent on the fields on the two tables being used - so even though the function may work fine when the programmer used it, later developers add fields to the tables and break it. I think Axapta used to get around this problem with a "Field Mapping" between tables so that if you want a field to be transferred between tables you map them at the table design level. It's been a few years since I worked on Axapata so I could be wrong about this - but I think this would be a much better solution.

But again, thanks for commenting and you raised some very valid points.