Unreferenced vs. Unused
Any FileMaker system that has been around for a while tends to generate a bunch of cruft, old stuff that isn’t being used anymore. It sure would be nice to get rid of it. But can we be sure that something isn’t used in any FileMaker system? Can FileMaker find unused scripts, field, and layouts etc? Well no, not exactly, but you can use some 3rd party tools, like our own FMPerception to look for things that are unreferenced. But before you do it is really helpful to understand the difference between “unreferenced and “unused”
What’s the Difference Between “Unreferenced” and “Unused”?
Simply put, when a FileMaker element is said to be “unreferenced”, it means that no other FileMaker elements reference it directly. However, it does not mean that the element is unused. There are lots of ways that something can be unreferenced but still actually be used by the system.
We can use programs like FMPerception to examine the DDR and determine without a doubt that something is “unreferenced”. However it is much more difficult, and in some cases impossible to tell if something is “unused”.
There are coding practices you can follow to limit your exposure to this problem. The good news is that using these practices is usually a good idea anyway. See below for more information on these practices.
What’s a Reference?
A reference is a direct link between two elements in a FileMaker system. For example, fields that are placed on a layout are “referenced” by the layout. The layout actually has a list of field IDs that are displayed on it.
This is important. The layout refers to those fields by their IDs, not their names. This is why you can change a field name without the layout getting confused about what fields to use. This is a “reference”.
Here are some other examples. A Set Field step “refers” to the field it is setting by its ID, not its name. A relationship refers to its predicate fields by ID, not by name. A custom function references other functions in its calculation by those Functions IDs, not their name. A Go To Layout script step may reference a layout by its ID. ( Note the “may”. See the next section for more info. )
The key is that a “reference” is a link between two elements that is defined during development and does not change during the normal operation of the program.
How Can Something Be Used If It Isn’t Referenced?
Under some circumstances, FileMaker can dynamically figure out, at runtime, what element it should use as the source or target of an operation. When this happens, the target element is not “referenced” by the other element. The state of the program at the moment of use determines what element is used.
The Go To Layout step is a good example. When you specify the Go To Layout script step, you have the option of directly referencing a Layout, or using a calculation to return either the name or the number of the layout. If you use the name or number option, you are not referencing the target layout directly. You are basically saying, “When this script runs, take the result of this calculation and figure out what Layout to go to based on that”. You are telling FileMaker to figure it out at runtime.
This means the link between script step and layout is dynamic. It can change during the course of the normal operation of the program. A layout that is targeted in this way is not “referenced”.
Is It Bad Practice to Write Code That Doesn’t Use References?
Not necessarily. It is a very useful and powerful technique. It can allow us to build abstracted and more portable code. However, there are consequences. You need to be aware of them and take steps to deal with them. If you don’t, then your code will be fragile and prone to bugs. It would be considered bad practice if you write code that doesn’t take this into consideration.
How Do I Know If I Am Using Reference-less Code?
The following functions and script steps are an example of code that does not require you to use references to other elements. If you are using any of these you may be writing code that doesn’t use references. I say “may”, because in almost every case you can change your code so it uses references again.
- Set Field By Name
- Go To Layout ( By Calculation )
- Open URL ( when used to trigger scripts)
- Go To Next/Previous Field
There are others. Script Steps like Go To Next/Previous Field don’t use references to find the target field. There are also Get Functions like Get(ActiveFieldContents) and Get(ActiveFieldName) that just use the current state of the system, in this case, which layout object has focus, to determine what field to use.
Here is another way to think about it. If you pick the element in a selection list, like with Go To Layout, or Set Field Script Steps, you are using a reference. In all calculation dialogs, when you enter a field name or choose the field name from the list, you are also using a reference. In all other cases, where you are targeting an element, you are not using a reference.
How Can I Add References Back to My Code?
It varies from situation to situation, and in some cases it’s really hard to do. Here is the basic idea. Always grab the name, (or number in the case of a layout by number) with a referenced link before you use it.
As an example, if you want to use the Set Field By Name Script step, use the GetFieldName(MyTable::MyField) function first to get the current name of the field and store it in a $variable. Then use the $variable in the Set Field By Name Script Step.
You can do the same with ExecuteSQL(). This is so important to do that most experienced developers wouldn’t consider using ExecuteSQL without GetFieldName() to calculate the names of the fields they are using in the SQL statement. Not only does it help you determine if the field is used or not, it also means changing your field names won’t break your SQL.
Here is a FileMaker calculation using ExecuteSQL that doesn’t use references.
Let( [ // create the sql statement as a single string sql = "SELECT FirstName FROM Customer WHERE Status=?" ]; ExecuteSQL ( sql ; "" ; "" ; "active" ) )
This calculation will produce exactly the same as the one above. But it references “Customer::FirstName” and “Customer::Status” directly. It has another advantage over the above calculation. It will continue to work if you change the field names.
Let( [ // get field names using GetFieldName firstNameFieldName = "\"" & Substitute( GetFieldName(Customer::FirstName) ; "::" ; Quote(".") ) & "\"" ; whereFieldName = "\"" & Substitute( GetFieldName(Customer::Status) ; "::" ; Quote(".") ) & "\"" ; // get Table names also with GetFieldName customerTableName = GetValue( Substitute ( GetFieldName(Customer::FirstName) ; "::" ; "¶" ) ; 1 ); // create the SQL Statement sql = "SELECT " & firstNameFieldName & " FROM " & customerTableName & " WHERE " & whereFieldName & "=?" ]; ExecuteSQL ( sql ; "" ; "" ; "active" ) )
You could use similar techniques with Evaluate() as well.
It is more difficult to do with Go To Layout (By Name or Number), but you can still do it. You can use a script to Go To Layout (By Reference), and then store its name or number in variables for use later.
These are just some examples. If you understand what a reference is you’ll be able to figure out when you are using it and when you aren’t. It’ll also become clear if there is anything you can do to get the robustness of references and the flexibility of unreferenced abstracted code.
Can I Make It Easier for Reporting Tools to Find Things That Are Unused?
There are times when there is nothing you can do, or when you can make the code far too brittle to use and too hard to maintain. It would be nice if you could at least let a tool like FMPerception know that you care about an element even though it isn’t referenced.
It’s pretty easy actually. You can just make references to those elements.
Here is an example for dealing with scripts that are not hooked to buttons or otherwise referenced, but still used manually from the Script Workspace. You can create a script called “Referenced Scripts”, and reference every script that is otherwise unreferenced by using a Perform Script step.
It’s probably a good idea to place an Exit Script step at the top so all those scripts won’t ever be run accidentally as a batch. You could also just comment out the Perform Script steps. FMPerception will see that as a reference even if they are commented out.
You could even attach that one script to a button on a layout so that it shows up as referenced itself.
You could do this with just about everything else, custom functions, fields, layouts, etc. Think of it as a way to document your solution while helping FileMaker find unused elements.
Can the Tools Get Better Helping FileMaker Find Unused Stuff?
Yes and No.
The tools can get better at finding some of the text based links between elements. We are looking into ways that FMPerception might be able to do this. But all that will do is reduce the false positives. It would designate fewer elements as unused. However, you will still need to consider each case carefully, since some of the reference-less links out there can not be statically analyzed. The only things a tool can tell you for sure is if an item is referenced. The opposite of referenced is not unreferenced, but rather “Undetermined”