The Art of Debugging

While chatting with other Appian experts, I had the idea to ask them for their tricks around debugging Appian applications. I heard many positive things that Appian improved a lot in recent versions. And still, there are general guidelines and tips&tricks that can make you a more effective debugger.

Debugging 101: A Methodical Approach to Resolving Software Issues

When it comes to resolving issues in software applications, even when built with low-code platforms like Appian, a methodical and structured approach is essential. Debugging can be a daunting task, but by breaking down the process into manageable steps, you’ll increase your chances of identifying and fixing problems efficiently.

The debugging process typically begins with understanding the issue itself – what’s not working as expected? What symptoms are being observed? This initial step sets the stage for further investigation. Next, it’s essential to gather relevant information about the issue, such as error messages, system logs, or user feedback.

From there, a logical and systematic approach is key:

  1. Reproduce the Issue: Attempt to replicate the issue in a controlled environment to ensure you’re dealing with a consistent issue.
  2. Gather Information: Collect relevant data points like error codes, log files, or screenshots of the problematic behavior.
  3. Analyze Symptoms: Break down the symptoms into smaller components and identify potential causes based on your knowledge of the system architecture and functionality.
  4. Inspect Code and Configuration: Review the relevant code snippets, configuration settings, and dependencies to spot any inconsistencies or anomalies.
  5. Test Hypotheses: Develop hypotheses about possible solutions and test and validate them systematically.

Throughout this process, it’s crucial to maintain a clear understanding of your goals, priorities, and constraints – both technical and business-related. By following these steps in a logical order, you’ll be well-equipped to tackle even the most complex software issues built with Appian or any other low-code platform.

Now, we’ll dive deeper into specific debugging techniques tailored for Appian applications.

Applications

https://docs.appian.com/suite/help/latest/monitoring_view.html

Make it a habit to visit the monitoring view, even with no specific reason to do so. Check for missing test cases, process errors, query performance and design hints.

Interfaces

Missing Line of Code Indication

When I see an error message in interfaces which does not indicate a line number, I typically just delete larger portions of the code. If the error message is gone, I undo my delete operations and repeat that with smaller sections inside that portion.

Debugging at Runtime

In situations I need to peek under the hood outside the interface designer, I am using a special interface component that can display raw data from rule inputs and local variables, as well as the data type and whether the data is considered null.

PFX_C_DebugField

Rule Input:

a!boxLayout(
  label: "DEBUGGING IN PROGRESS ...",
  style: "ERROR",
  contents: {
    a!richTextDisplayField(
      label: concat(
        "Type: "
        typename(typeof(ri!input)),
        " | Items: ",
        count(ri!input),
        " (incl. NULLs)",
        " | isVoid: ",
        rule!PFX_IsVoid(ri!input)
      ),
      value: a!richTextItem(
        text: tostring(ri!input),
        style: "EMPHASIS"
      )
    )
  }
)

PFX_IsVoid

if(
  a!isNullOrEmpty(ri!value),
  true,
  a!localVariables(
    local!typeNum: a!refreshVariable(
      value: runtimetypeof(ri!value),
      refreshAlways: true
    ),
    
    /* Strings */
    if(
      local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Text',
      /* Ignore white space */
      len(trim(ri!value)) = 0,
      
    /* List of Variant */
    if(
      local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Variant?list',
      all(a!isNullOrEmpty(_), a!flatten(trim(ri!value))),
      
    /* Lists */
    if(
      /* Try to take first item in list and cast it to a list of itself */
      /* then compare its datatype to the incoming data type */
      local!typeNum = runtimetypeof(cast(runtimetypeof({index(ri!value, 1, null)}), ri!value)),
      
      /* Flatten takes care of lists in lists in lists in ... */
      /* List has zero non-null items, length() already ignores null values*/
      length(a!flatten(ri!value)) = 0,

    /* Map */
    if(
      local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Map',
      /* Has no keys at all or all values are null */
      or(
        length(a!keys(ri!value)) = 0,
        all(a!isNullOrEmpty(_), index(ri!value, a!keys(ri!value), null))
      ),
      
    /* DataSubsets or Maps returned by queryRecordType */
    if(
      or(
        local!typeNum = 'type!{http://www.appian.com/ae/types/2009}DataSubset',
        and(
          local!typeNum = 'type!{http://www.appian.com/ae/types/2009}Map',
          contains(a!keys(ri!value), "data"),
        )
      ),
      a!isNullOrEmpty(ri!value.data),

      /* No idea what this could be so it is considered to not be null */
      false
    )))))
  )
)

Expressions

Data Wrapped in Any Type

In some situations, like when using queryEntity(), you get your data wrapped into the ANY type. Appian indicates this by a different type name, like Any Type (Number (Integer)) instead of Number (Integer). When you now try to compare that data to a non-wrapped, you will see an error message indicating a type mismatch.

Lists of Lists

In certain situations, like when using a!foreach(), you might get a list of lists where each list contains a single item. Keep in mind that curly brackets create a list, also inside a foreach().

Now, sometimes it is difficult to see that you deal with a list of lists, but not with a list of items. Keep this in mind when you try to get the number of items in a list, and you get a weird number. Look for any unnecessary curly brackets and use a!flatten() as a last resort.

Target is Missing

When you see the error message “Target is Missing”, it means that you missed a comma somewhere in your code. Keep in mind that Appian SAIL is a functional language and all code we write is actually just parameters for calling a function. And parameters are separated by comma.

Process

In general, when trying to locate issues in a process model, make sure to follow the data flow. For each transition …

  • from the start form to process variable
  • from process variable to node variable (inputs) and back (outputs)
  • from node variable to interface rule input
  • in and out of sub processes

… have a clear goal, know your expectations for each individual transition, and validate this against the actual behaviour you observe. And most important, do this one step after the other!

Record Data Mishaps

Record data does not work for any kind of date or time values, on timer, deadline, escalation, or exception configurations. Make sure to calculate the value beforehand, store it in a process variable, and then use that to configure the timer.

Debugging Failed Process Instances

When debugging a process, monitor that instance showing issues. Then you can edit this specific instance and restart that failed node. Just right-click the node. Once I identified the issue, I typically go back to the model and fix it. But you can even save that modified process instance as the new version of the model.

https://docs.appian.com/suite/help/latest/Monitoring_and_Editing_Processes.html

Finding Variables

Trying to find where specific variables are used in a process model is a great use case for the Generate Documentation function.

Use your browsers search function (CTRL/CMD + F) to find variables, fields or other things you are looking for.

Expression Errors

Oliver Power pointed out a specific issue with how Appian checks SAIL syntax. The expression/interface designer is doing this a tiny bit different from when publishing a process model.

He uses the following method:

The thing is Appian is really good at helping you debug now.

The one thing where it fails is, when you have invalid expression language somewhere in a process model (whether it’s direct or within a rule).

When you save the model, you’ll get a generic “problem saving process” and no actual error message.

Best way to navigate this I can find is click “File”>”New Process Model” and copy and paste your model into the new model.

Then piece by piece, delete nodes from your existing model until you can save the model successfully.

Once this is done and you’ve isolated the problem node, you can fix the issue (like i say, it’s usually invalid expression language) .

Then you can copy and paste everything back from your temporary new process model (“Untitled_X (Draft)”).

Summary

Appian got pretty good at supporting us when debugging. The tricks described in this post, will make you even more effective.

Do you miss your best kept debugging secret? Please let me know and I will add it to this post.

Rock your bugs 😉

Leave a Reply