In the previous chapters, we learned about Patterns that help us design objects and object elements. Now, we will start adding code to make them work together.
There are Design Patterns about structuring code, but most of the things that we will discuss in this chapter are considered best practices for writing code. Some of them are fundamental basics of object-oriented programming (OOP), such as Encapsulation, although Encapsulation is older than OOP and applies to Microsoft Dynamics NAV as well.
Good coding practices will improve the maintainability of your application, and make it easier to work with a team of developers on the same application, or share the code outside of your team. The latter is very common in the Microsoft Dynamics NAV ecosystem, and starts with Microsoft sharing their code with NAV partners. Partners then again share the code with other partners and also customers. Microsoft Dynamics NAV is a popular ERP system with customers who require a flexible system that can be easily customized.
Best practices for writing code in any language start with a set of programming guidelines. This is important for any development team, but crucial for projects that share code with different teams, such as Dynamics NAV.
The programming guidelines for C/AL have hardly changed in 30 years. For Navision Software A/S and later, Microsoft has always had very clear coding guidelines. Throughout the lifecycle of the application, they have been very consistent.
In January 2015, Microsoft released an updated set of guidelines, based on NAV 2015. We will discuss the most important ones.
You can find the guidelines at https://community.dynamics.com/nav/w/designpatterns/156.cal-coding-guidelines, and https://msdn.microsoft.com/en-us/library/ee414237(v=nav.80).aspx.
The C/AL compiler checks our code for programming errors such as missing object references, and BEGIN
/END
or REPEAT
/UNTIL
statements that are wrong.
The Microsoft Dynamics NAV Development Environment has a limited warning mechanism that only checks the C/AL functions that used to be part of the Classic Client, but no longer worked in the three-tier stack. The examples are BEEP
and ENVIRON
:
The code design rules are implemented to add an extra layer of rules that are not enforced by the compiler or generate warnings but when applied, they improve the readability of the code.
Unused Variables |
Every variable that is declared global, local, or as a parameter should be used. Unused variables should always be removed by the developer. |
By Reference |
When assigning a variable as parameter, we have the option to declare the variable as By Reference. When this is applied, changes to the variables also apply to the original, since they share the same memory space. In Microsoft Dynamics NAV, the column is called By Reference should only be enabled when there is a clear intention of changing the variables. |
Match Placeholders |
Although C/AL does not support the function with a variable arguments list, there is a limited set of commands that allow the use of multiple parameters. The examples of these functions are When applied, these functions should be called with placeholders in a text string called |
Set Variables |
Variables should be used in business logic. If variables are set to a specific value but don't influence the decision tree, they should be removed. For example:
Count := 0; Vendor.SETFILTER("No.",FilterStr); IF Vendor.FINDSET THEN REPEAT "User ID" := USERID; "Vendor No." := Vendor."No."; IF INSERT THEN Count += 1; UNTIL Vendor.NEXT = 0; This is an example of a potential boat anchor anti-pattern. We will discuss more about anti-patterns in Chapter 6, Anti-patterns and Handling Legacy Code. |
Within C/AL there is no compile time to check for a possible overflow scenario. An overflow can occur when a large string is moved in a variable or field with limited storage capacity. The following screenshot shows an error when the length of the string usage is exceeded:
Since Dynamics NAV version 2013 R2, it is possible to have Text variables with unlimited length, which are mapped to the DotNet
string datatype. However, when writing to the database, the maximum length of a string is 250 characters.
When programming conditions such as IF
/ELSE
or CASE
/OF
, and FOR
/DO
, we add complexity to our code.
The Cyclomatic Complexity is a methodology to calculate the complexity of your C/AL functions. There are multiple officially-documented algorithms, and for Dynamics NAV, we can use Cyclomatic Complexity Three, or CC3.
This means that each IF
/ELSE
/CASE
and FOR
condition gets one point. After adding up all the points, a function gets a score. The higher the score, the more complex the function is and therefore, high scores should be avoided.
You can find more information on Wikipedia at https://en.wikipedia.org/wiki/Cyclomatic_complexity.
Internally, Microsoft programmers working on the product use a threshold of 25 points. I would advise you to use a maximum value of 10.
Each function that we write in Dynamics NAV, whether it is a built in trigger or manually added function, should not exceed the size of a normal modern computer screen.
When using Microsoft Dynamics NAV 2015 Development Environment on a HD screen, the maximum size of a function should not exceed 40 lines.
Internally, Microsoft programmers use a threshold of 100 lines. Since a lot of legacy functions in Dynamics NAV have much more than this, another rule internally used at Microsoft is when having to add one line of code to a function that has over 100 lines, two other lines must be moved out, and refactored into a function.
This is considered to be a good methodology for refactoring while avoiding disturbing the upgrade as much as possible.
To avoid complexity in a single function, there is a maximum threshold of complex data types in C/AL, such as Record, Page, Query, and Codeunit, as shown:
Since Navision version 2.01, 2.60, and 3.00, the application can be translated independently of the code. This can be done using the Text Constants. They can be either declared globally or locally. The following screenshot show the Text Constant tab:
These Text Constants should always be used, unless the text constant is used internally only by the application, as illustrated in the following screenshot:
The naming of the Text Constant should be self-explanatory, indicating if it is used as Message, Error, Question, or something else. Errors and Message constants should end with a period, and a Question should end with a question mark always.
When working with multiple developers on a single codebase and sharing software as open code with external partners, using a generic set of rules regarding readability is important from a maintenance perspective.
This includes a long list of small things such as using spaces, comments, white-lines, BEGIN
/END
, TRUE
/FALSE
, indentation, and parenthesis:
When applied consistently, these rules will enable the readability of your application, making it easier to share code with people both within and outside your organization.
Although Microsoft Dynamics NAV Development Environment does not have a built-in data dictionary that is enforced by the compiler, it has very strong naming conventions that have been stable throughout the lifecycle of the product:
When it is required to have multiple instances of the same complex datatype, they should be explained rather than numbered:
MoveReservationFromSalesToPurchase(ReservationEntrySales) WITH ReservationEntrySales DO TRANSFERFIELDS(ReservationEntryPurchase)
Frequently used variable names in Microsoft Dynamics NAV have approved abbreviations. This can enhance the readability of the code, because we avoid very long variable names.
This list contains the abbreviations used by Microsoft Developers, which is available at https://community.dynamics.com/nav/w/designpatterns/162.suggested-abbreviations.