alguidelines.dev - Business Central Design Patterns – 2. Anti-Patternshttps://alguidelines.dev/docs/navpatterns/2-anti-patterns/Recent content in 2. Anti-Patterns on alguidelines.dev - Business Central Design PatternsHugo -- gohugo.ioen-usDocs: Nav Upgradehttps://alguidelines.dev/docs/navpatterns/2-anti-patterns/nav-upgrade/Mon, 01 Jan 0001 00:00:00 +0000https://alguidelines.dev/docs/navpatterns/2-anti-patterns/nav-upgrade/ <h2 id="anti-patterns-in-nav-upgrade">Anti-Patterns in NAV Upgrade</h2> <p><em>By Carlos Raul Garcia and Bogdana Botez at Microsoft Development Center Copenhagen</em></p> <p><a href="upgrade.png"><img src="upgrade.png" alt=" "></a></p> <p><strong>Context</strong>: when NAV is upgraded, whether on-premises or in the cloud, developers have the chance to write upgrade code to move data across changing data structures. Writing good quality code will help successful upgrades.</p> <h3 id="general-on-upgrade">General on upgrade</h3> <p><strong>Problem:</strong> assuming that the upgrade table contains data.</p> <p>If the table is empty, it means that either the upgrade has run, or there was no data in the original tenant; in both cases, the upgrade code should exit immediately.</p> <p><strong>Solution:</strong> if using an upgrade table, always validate that the table contains data before doing anything.</p> <h3 id="upgrade-code-can-it-be-rerun-safely">Upgrade code, can it be rerun safely?</h3> <p><strong>Problem</strong>: if the upgrade code is not written in a way that makes it runnable twice (<a href="http://stackoverflow.com/questions/1077412/what-is-an-idempotent-operation#1077421">idempotent</a>), then several failures can happen, including something as critical as data corruption.</p> <p>In on premise NAV installations, if something fails at upgrade, there is no way to run only the &ldquo;remaining&rdquo; tasks. You will need to run the whole upgrade again, and might end-up with data that you cannot trust.</p> <p>What about the cloud? In Platform As A Service (PaaS), in some situations, the upgrade code needs to be run twice (for example, when moving tenants from a broken/frozen VM to a healthy one).</p> <p><strong>Solution:</strong> Make sure each of your upgrade procedures only kicks in if it didn&rsquo;t run before.</p> <p><strong>Examples</strong></p> <p>The examples below have happened in real live NAV PaaS upgrade:</p> <p><strong>Table data overwrite</strong></p> <ul> <li><strong>Problem</strong>: at upgrade, a new column has bee­n added to a table and initialized with default values. In the meanwhile, during production, some of the default values are changed to production real life values. The second time the upgrade runs, those values will be overwritten with defaults, any personalization lost.</li> <li><strong>Solution:</strong> before initializing with default values, check if non-default values exist.</li> </ul> <p><strong>Crash on math operations</strong></p> <ul> <li><strong>Problem:</strong> one tenant upgrade managed to divide by zero, by assuming a non-zero value.</li> <li><strong>Solution:</strong> don&rsquo;t assume values can never be zero, always check before using them in divisions.</li> </ul> <p><strong>Use of external components</strong></p> <ul> <li><strong>Problem:</strong> a one-time registration through web services to an external service failed when attempting to register a second time.</li> <li><strong>Solution:</strong> check if already registered, before attempting again.</li> </ul> <h3 id="parallelism">Parallelism</h3> <p><strong>Problem</strong>: Upgrade procedures can be run in parallel, causing issues when different procedures attempt to modify the same table at the same time.</p> <p>When modifications to the same table are being made from two or more different procedures, there is no guarantee on sequential run, or on a certain order they will be run in.</p> <p><strong>Solution</strong>: if sequential or ordered execution is needed, make the affected upgrade procedures local and call them all, in the desired sequence, from a public upgrade procedure.</p> <h3 id="access-to-cloud-machines">Access to cloud machines</h3> <p><strong>Problem:</strong> dependencies on manual installation steps do not fit in the cloud.</p> <p>If Dynamics NAV is installed on-premises, then any additional setup (like dependencies of external dlls, manual configuration steps etc.) can be done manually or semi-manually by the IT admin, at first setup and upgrade.</p> <p>In the cloud, NAV partners don&rsquo;t have access to the machines &ndash; hence they cannot deploy and configure those external dependencies as they did in the old on-premises installations.</p> <p><strong>Solution:</strong> Don&rsquo;t assume you will have access to PaaS or SaaS machines. Build your solution in such a way that it doesn&rsquo;t depend on executing manual configurations on the host machine.</p>Docs: Reusable Bugshttps://alguidelines.dev/docs/navpatterns/2-anti-patterns/reusable-bugs/Mon, 01 Jan 0001 00:00:00 +0000https://alguidelines.dev/docs/navpatterns/2-anti-patterns/reusable-bugs/ <p><em>By Bogdana Botez, Andreas Moth, Eric Wauters (waldo), Elly Nkya, Nikola Kukrika</em></p> <p><em>April 1st, 2015</em></p> <h2 id="abstract">Abstract</h2> <p>It is difficult to come up with new and different bugs in each release, and it is a waste of resources to not use the ones which already had proven successful in the past. To avoid reinventing the wheel, we propose to come up with new innovative ways to create bugs that are reusable and generic enough to be used in various places of the application.</p> <h2 id="examples">Examples</h2> <p><strong>Option Strings</strong></p> <p>One example of a reusable bug is to find a heavily used table, like table 36 Sales Header, and observe one of the more important fields of type Option, for example Document Type. The OptionString property has the following value: Quote,Order,Invoice,Credit Memo,Blanket Order,Return Order, which you should update to add one option, for example Transport Order in the beginning.</p> <p>The main benefit of this reusable bug is that code changed in one place, has impact in multiple sides on the application: document creation and handling, posting etc.</p> <p><strong>Application Management</strong></p> <p>Codeunit 1, ApplicationManagement is a good place for creating reusable bugs. A simple INSERT on the RecRef on the first line of OnDatabaseInsert will create a sure and versatile bug that is reusable all over the application. Redundancy on database insertion ensures that data is surely conveyed to the data storage layer, twice.</p> <p><strong>God objects</strong></p> <p>We are considering to no longer make localization builds for countries. It consumes lab resources to keep running multiple country builds. Instead, we will merge everything into one single build. This is a bigger scale implementation of the <a href="http://en.wikipedia.org/wiki/God_object">&ldquo;God object&rdquo;</a>. Inside this unique build, we will use IF statements and sporadically CASE statements to select each country&rsquo;s behavior. For more help on how to use IFs, see also the IF .. THEN|ELSE C/AL Coding Guideline.</p> <p><strong>Field length economy</strong></p> <p>When you post a document, make sure to transfer data to a field that is smaller than the field you are assigning from. This will not immediately be caught and will only hurt a subset of the customers who uses Microsoft Dynamics NAV to its fullest.</p> <h2 id="guidelines">Guidelines</h2> <p>When bug addition is not entirely obvious, there is a second way to approach the problem. By following some general coding best practices like the ones described below, reliable bug innovation is facilitated and can come naturally as a collateral effect.</p> <p><strong>Code structure</strong></p> <p>Put everything in one function and only use comments to explain the structure of your code. And don&rsquo;t use functions - because this only complicates things&hellip; having to navigate from function to function, and completely lose track of where you are in the business logic.</p> <p>On top of that .. put everything in one codeunit. Because also that will simplify and make your structure more readable.</p> <p>Use Hungarian Notation on your variables, because at any time, you need to know what type, and what context your variable is on.</p> <p>Declare all your functions and variables global, so they are available at any time.</p> <p><strong>Don&rsquo;t do Unit Testing</strong></p> <p>Unit testing adds complexity and extra time to the stuff you&rsquo;re doing. Also, it eats up extra codeunits which means: it costs money. You will never be able to foresee all scenarios possible, so you&rsquo;re destined to forget and not test everything. So you will save time in not doing unit testing.</p> <p><strong>Never add images to actions</strong></p> <p>Because there is an image by default. When you don&rsquo;t provide an image on an action on a page, the application will foresee a ball&hellip; and when you never do it, your application will have a very consistent way of showing your actions, by providing that picture of that ball. On top of that, you&rsquo;ll save time.</p> <p><strong>Do not care about ControlIDs</strong></p> <p>When you&rsquo;re doing development of your product, do not care about ControlIDs, and just leave the Offset ID to the default value of 0. This way, when merging, you will receive nice notifications, saying both you and Microsoft have added functions in that objects. You can use this feature to document all these places.</p> <p><strong>Hooks</strong></p> <p>Never apply the hook pattern. Hooks will only reduce upgrade time. This means, you will only shortly enjoy using the AMU (Application Merge Utilities). The more you change in default application, the longer it takes to upgrade, the longer you will enjoy the toolkit</p> <p>This can be taken one level higher. Simply you are not hardcore if you do not use notepad to resolve all of the merge issues.</p> <p><strong>How to use RecRef</strong></p> <p>Why fuss around declaring specific table variables, just generalize, all you need is one, two, or perhaps three RecRef variables, with a few IFs and CASEs here and there for reflection, to carry you all the way.</p> <p><strong>Arguments</strong></p> <p>Using only a few arguments on the functions is a sign of a weak developer. Stick in as many arguments as possible on the function, even if you are not using them, they could be useful in the future.</p> <p><strong>Just another field / action</strong></p> <p>Thinking of the design is overrated, each problem can be solved by adding an additional field or the table/page or with adding another action. We all know this has worked well in the past.</p> <p><strong>Reusability</strong></p> <p>We have decided that each time we fix a bug, we now also explain how it can be applied as a pattern. We then use anti-virus software to search for these patterns, to make sure we do not re-introduce these bugs anywhere else in NAV.</p> <p><strong>Business logic placement</strong></p> <p>As a best practice, we have also decided to move code into pages. Business logic should no longer be in tables and codeunits, but instead pages should know and be aware of the context and update it accordingly. As opposite to tables and codeunits, pages are aware of the context.</p> <h2 id="conclusion">Conclusion</h2> <p>Happy April Fools&rsquo; Day.</p> <p>Disclaimer: this is inspired from IETF documentation published on April 1st, like for example the revolutionizing <a href="http://en.wikipedia.org/wiki/IP_over_Avian_Carriers">IP over Avian Carriers</a> standard.</p>