Planning for Trouble
The previous sections suggested a few approaches you can follow to make your programming easier to troubleshoot. But errors will happen. As you build systems, you need to expect errors, and plan for how to handle them. Many programmers (too many) make the mistake of assuming that things will always work correctly. Their programs behave perfectly well, as long as nothing unexpected happens. But when a user enters data they didn't expect, or a network connection drops, suddenly the program ceases to behave gracefully, and its behavior becomes what we call (charitably) "undefined."
Let's take a simple example. You have a FileMaker system that periodically needs to import data from some other source. Imagine that the FileMaker system contains a table of manufacturers, and the manufacturer data is actually being fetched from an Oracle system via an XML Import. The update is intended to be a destructive update, meaning that the new data completely replaces the old. You want to perform all this work via a script, so you write the following (nai[um]ve) script:
Go to Layout ["Manufacturers"] Show All Records Delete All Records [No dialog] Import Records [No dialog: http://my.mfg.com/mfg_update?code=3]
That seems right. Find all the current manufacturers, delete them, and import the new ones. But it's not right; it's dead wrong. What if the import step fails (for example, because the web server at http://my.mfg.com is down for maintenance)? Well, you've already deleted all your manufacturers, and now you have no way to fetch new ones, for who knows how long. Much better to go on with a slightly outdated manufacturer list than none at all.
The deletion step could fail as wellfor example, if users had one or more records locked. If this happens and you don't catch it, you end up with duplicate manufacturers, which could be a serious problem.
The right way to do this is to perform the import first. If and only if it succeeds, you can then find the old records and delete them. The script should look like Listing 17.1.
Listing 17.1. Correct Import Script
Import Records [No dialog: http://my.mfg.com/mfg_update?code=3] If[ Get( LastError ) <> 0 ] Exit Script Else Show Omitted Only Delete All Records [No dialog] End If |
This still isn't perfect, but it's much better. To make it better still, you'd need to examine what the exact error was during the import to decide whether some records had been imported and needed to be deleted. But it prevents the worst case, which is an empty manufacturer table.
For more on FileMaker's XML Import feature, which we posited as a possible source for the data in this example, see "XML Import: Understanding Web Services," p. 683. |
It's easy to produce other examples of this kind. They all boil down to the same logical error: the unwarranted assumption that a specific step is going to work. The only time that it's okay to make this assumption is if there's no significant penalty for being wrong. For example, imagine you have a script that finds all invoices over 30 days old and changes their status from Current to Past Due. It's possible that the search will fail and find no records. But in that case it's innocuous to go on and perform the Replace stepit will operate on a found set of zero records and will have no effect.
So, one of the most important ways to avoid software defects (the graceful term for bugs) is to be aware of all the possible failure points in your system, and, most importantly, calculate the consequences of failure. Good programmers do this instinctively. They have a clear sense of what will happen if some element of their program fails. The question is never a surprise to them, and they almost always know the answer.