Developer Tips

How to Build a FileMaker Script That Self-Documents

Build self-documenting FileMaker scripts with clear contracts, structured errors, useful comments, and logs that explain what actually happened.

A self-documenting FileMaker script is not one with a comment above every step. It is a script whose purpose, inputs, decisions, side effects, errors, and result can be understood without reconstructing the original developer’s train of thought.

Comments help, but the strongest documentation is executable: validate the parameter, name variables for their role, handle errors where they occur, return a structured result, and log enough context to explain a failure later.

Give the FileMaker script an explicit contract

Start the script with a short header comment:

# Invoice | Post payment
# Purpose: Apply one payment to an open invoice.
# Parameter: JSON object with invoiceId, paymentId, amount, and requestId.
# Result: JSON object with ok, code, message, invoiceId, and requestId.
# Side effects: Creates a payment application and updates invoice balance.

This is more useful than a change log inside the script. Git, Snapshot history, or release notes should explain when code changed. The header should explain how to call the script now.

Then make the contract real:

Set Variable [ $parameter ; Value: Get ( ScriptParameter ) ]
Set Variable [ $invoiceId ; Value: JSONGetElement ( $parameter ; "invoiceId" ) ]
Set Variable [ $paymentId ; Value: JSONGetElement ( $parameter ; "paymentId" ) ]
Set Variable [ $amount ; Value: JSONGetElement ( $parameter ; "amount" ) ]
Set Variable [ $requestId ; Value: JSONGetElement ( $parameter ; "requestId" ) ]

If [ IsEmpty ( $invoiceId ) or IsEmpty ( $paymentId ) or $amount ≤ 0 ]
  Exit Script [ Text Result:
    JSONSetElement ( "{}" ;
      [ "ok" ; False ; JSONBoolean ] ;
      [ "code" ; "INVALID_PARAMETER" ; JSONString ] ;
      [ "message" ; "invoiceId, paymentId, and a positive amount are required" ; JSONString ] ;
      [ "requestId" ; $requestId ; JSONString ]
    )
  ]
End If

The next developer does not need to infer which keys are optional. The caller gets a result it can handle without parsing a sentence.

Use names that explain scope and role

FileMaker’s variable prefixes already communicate scope: $ is local and $$ is global. Do not waste that advantage with names such as $x, $data, or $result2.

Prefer:

  • $invoiceId for a single record identifier
  • $invoiceFoundCount for the result of a find
  • $originalBalance for a value captured before a write
  • $errorCode and $errorDetail for FileMaker error evidence
  • $resultJson for the final script result

Use global variables only when state truly must survive beyond the script. A $$currentInvoice variable may feel convenient, but it makes the script depend on invisible session history. Passing the invoice ID as a parameter makes the dependency visible and testable.

FMDojo Code Chat can review a script for variables that are read before they are set, globals that could be local, and names that no longer match their use. With an active Snapshot, that review can also distinguish variables from real field, layout, and script names.

Put comments at decision boundaries

Good comments explain why a branch exists:

# Recheck the balance after the record lock because another user may have
# posted a payment since the confirmation screen loaded.

Weak comments narrate the next line:

# Go to the invoice layout
Go to Layout [ “Invoice Utility” (Invoice) ]

Comment before a workaround, a destructive operation, an external call, a server-only branch, or a rule that is not obvious from the schema. Avoid turning comments into an alternative implementation that can drift away from the actual steps.

Use section comments consistently:

# --- Validate input
# --- Load and lock invoice
# --- Apply payment
# --- Verify totals
# --- Return result

That makes long scripts scannable in Script Workspace and gives reviewers stable landmarks.

Capture errors immediately

Get ( LastError ) reports the most recent script-step error. Another step can replace it, so capture it immediately after the step you care about:

Perform Find [ ]
Set Variable [ $errorCode ; Value: Get ( LastError ) ]
Set Variable [ $errorDetail ; Value: Get ( LastErrorDetail ) ]

If [ $errorCode = 401 ]
  # Return a not-found result
Else If [ $errorCode ≠ 0 ]
  # Return an unexpected-find-error result
End If

Use Set Error Capture [ On ] when the script owns the response to expected errors. That does not mean ignoring them. It means replacing FileMaker’s dialog with an intentional branch.

For server-side scripts, verify that every step is supported. Claris documents that an unsupported server-side step returns error 3 and execution can continue, which is exactly why an explicit error check matters. A scheduled script that skips a step and keeps going can produce a misleading “completed” result.

For deeper diagnostics, Set Error Logging [ On ] can add script errors to the appropriate log when server settings also permit it. Include safe custom debug information such as the request ID, script phase, and non-sensitive record key. Never log passwords, tokens, or full customer payloads.

Return one structured result

Every exit path should return the same basic shape:

JSONSetElement ( "{}" ;
  [ "ok" ; $ok ; JSONBoolean ] ;
  [ "code" ; $resultCode ; JSONString ] ;
  [ "message" ; $resultMessage ; JSONString ] ;
  [ "invoiceId" ; $invoiceId ; JSONString ] ;
  [ "requestId" ; $requestId ; JSONString ]
)

The caller should not need special parsing for success, validation failure, record-not-found, and unexpected FileMaker errors. Keep a human-readable message, but make decisions from ok and code.

If a subscript returns structured JSON, check its contract immediately. Do not let a failed child script become an empty variable that causes a confusing error twenty steps later.

Review the script as a system

A script can read well and still be wrong because its layout context, found set, privileges, or server compatibility is wrong.

Before release:

  1. Confirm every layout and table occurrence against the current solution.
  2. Test valid, missing, malformed, and duplicate parameters.
  3. Test no-record and record-lock outcomes.
  4. Test the exact runtime: Pro, Go, WebDirect, Data API, or FileMaker Server.
  5. Verify the final record state, not only the script result.
  6. Read the generated logs and make sure they explain a forced failure.

FMDojo Snapshots make the schema and script inventory reviewable. Code Chat helps pressure-test the parameter, error, and result contracts. If the script participates in a deployment, use the Snapshot diff and FM Deploy review path so the documentation includes what changed, not only what the script claims to do.

The official Claris references for Get ( LastError ), server-side scripts, and Set Error Logging are the right source for runtime behavior.

What is new in FM Dojo related to this

FMDojo can review scripts against an active Snapshot, compare script revisions, and keep FileMaker object names visible in Code Chat. Use Snapshots to inspect the current solution and Code to review the script contract and error paths.