I love a nerdy title and I love learning about cool new smart stuff.
But before you continue reading I want to say thanks to my colleagues at ForNAV, especially Michael Nielsen. Without him pushing me (again) to step out of my comfort zone this would not have been possible.
Yesterday I visited the Microsoft office in Lyngby for the first time in a very, very long time to talk about our project to split up the base app. We also talked about something else, but that will be another blog. (Soon…).
I often wonder how I find myself in situations like this. On my day off I find myself behind my desk doing something which is not my responsibility, nor am I getting paid to do it.
If you say A, you also have to be willing to say B and C. This is what my parents told me when I was young so when I promissed Bugsy and Jesper to look into making the Business Central Base Application smaller I decided I may as well make it into a decent project.
Social media is fantastic and it can be used in many different ways. Where most people are mostly consumers others use it to ask questions. Some share knowledge and experience and gain loyal followers. This is pure awesomeness and mostly rewarded with the Microsoft MVP award for those who don’t give up.
Very few succeed in using social media to drive change. Those who do are known by many such as Elon Musk and Greta Thunberg.
So to continue where we left off yesterday when I did a pull request on the Business Central System App project on GitHub.
After my pull request someone at Microsoft did a code review in order to make sure quality standards are respected. Here are the details:
I have to admit something. First of all, this is not actually my code. It came from my colleague Michael Nielsen. Secondly this blog post is a bit orchestrated since Jesper and I discussed in Antwerp at NAVTechDays how to promote people contributing.
Working with Microsoft is pretty tough since your doing code for millions of users, not just one customer. Business Central should be easy to use and if you call the culture info with for example 0 or 80,943 you’ll get an error explaining that the language does not exist but not in an easy to read way.
I’m tempted to follow the idea to wrap the call in a TryFunction, but that means introducing a readeable error message, which includes translation and I am worried that will delay my pull request.
An alternative is to wrap it into a try function and return the orriginal integer if the call fails.
What do you think? It proves to me that coding for millions is a lot harder than it looks.
With the recent Wave II release of Business Central we also got the first wave of Open Source in our beloved NAV/BC product.
This means that rather than making customization for one specific customer or ISV you can now have this pushed back into the product and stay there forever.
Today I did my first Pull Request and I wanted to share how I did that.
What did I need?
With reports, especially documents that go out, translations are key. In the next release of our ForNAV extension we wanted to add a new feature where you can translate invoices by simply adding captions to a table or import them from an Excel sheet or Azure Blob Container file.
With translations there are rules that allow you to inherrit languages from related languages if they are related. For example Flamish (NLB) can be inherrited from Dutch (NLD).
Especially in Europe this drastically reduces the number of translations and you only have to manage exceptions where terminology is different. (Trust me, this is the case with Flamish and Dutch).
I actually only need one line of DotNET code
Normally you are not allowed to do DotNET, but System App has target set to OnPrem so simple usages of DotNET are actually allowed. (I hope).
Fork & Clone
The first step is to log into GitHub and Fork the Microsoft ALAppExtensions project and clone this to your Visual Studio Code
Branch & Publish
Then you do the change. Remember Microsoft has this two-layer model where your real code is in the implementation.
Pull Request & License
The final step, or at least where I am now, is to do a pull request and sign the license.
After this you need a code review and this is what I am waiting for right now.
This is a blog that I wanted to write for quite a while, but it’s only until last week or so that I think I’ve found what I like to call, a “best practice” or if you will, a “Design Pattern”.
What is this all about? With the move from C/AL to Visual Studio Code we also moved to file based instead of stored in database.
This means we also have to give these files a name and we have the option to organise the files into folders, or subdirectories if you are a born-in-dos generation.
If you follow the Microsoft guidelines and/or use the tooling from Waldo your extension your extension will probably look something like this:
Although this works perfectly there are a few issues with this that I don’t like. I’ll try to explain myself.
The Object ID is still there. Not perse a big problem but in the cloud they don’t mean anything anymore except making the object Unique. In the past, we as developers tried to use object ID’s in a smart way, like make sure that table & page have the same ID, or make sure that Header and Line tables have adjacent ID’s so Object Designer in C/Side would show them nicely grouped.
In most projects as well as in Microsoft’s BaseApp this is no longer true and possible. We have too much legacy to trust sorting and grouping objects by ID.
If you prefixed your objects in C/Side and run the conversion your filenames look something like this.
To me this looks like a horrible waste of the first 15 or so characters of your filename. I tried it for a while and it’s very hard to work with.
For reasons of legacy object names in AL can only have 30 characters. On top of this you’ll loose 3 or 4 on the TLA you need to mandatory put in my Microsoft. This often leads to horrible object names which are then converted to your file name
Lack of Intent
If all of your projects have the same object tree it does not make it very clear what kind of extension you are looking at. By opening the project we can only recognise it as an AL project but we cannot see what it does.
I can hear you say, so what? What do you suggest we do Mark?
When I opened the BaseApp in AL I got the first Aha moment on how to solve this puzzle. To my surprise Microsoft was not following their own guidelines.
It would not be the first time they don’t follow their own guidelines and at first it pissed my off a bit. But only untiil the beauty of this naming pattern struck me just like that.
With these naming guidelines you can automagically see what belongs together regardless of the Object ID and Object Type
Old Naming Conventions
For those of you who remember the old Solution Developer materials will also know that there are strickt naming guidelines. Like the Table Name is singular, the Page Name is Plural. If you have a List and a Card you name them like that etc, etc, etc.
If you were religious about these rules and follow this pattern your project suddenly makes much more sense to look at.
So I hear you say, what about subfolders? The BaseApp has over 6000 files and this naming does not make it easy to work with if everything is in one root folder.
The logical answer here is to apply common sense. Group your objects together if they belong together.
This also gives your project a clear intent. I can see by looking at the folder structure that this is a report pack with Labels, Sales Taks reports, Financial reports, and so on, and so on.
If you remove the abbreviations and then look at for example a Journal the pure beauty of this way of working with files becomes as clear as sunshine on a cloudy day.
See how nice the Batch, template and register are grouped together and it’s very easy to find the file you need.
One can debate if in this specific case the word “Example” is overkill, but if you think for a moment it would say “Item” I’m sure to think you agree with me.
Call to action
To me this is another indication of how smart one of the old Navision rules were and it looks to me that Navision was born in the cloud back in the late 1980ies. They just had not invented the cloud yet.
Use your brain, think out of the box and be surprised.
When you try to convert existing C/Side objects to AL the first attempts are typically done while scoping OnPrem. This gives an overview of the errors to be fixed w.o things like DotNET.
In this phase you typically switch between C/Side and Visual Studio Code all the time fixing the errors one-by-one and reconverting.
Once the errors are fixed the software can be tested and deployed using either Docker or a local install.
This is a very safe way of ensuring there is a migration path for your existing customers what is not overly complex and thus affordable from a TCO perspective.
The next step is moving to AppSource and this is where you need to remove all the DotNET dependencies.
The biggest issue with DotNET is that it’s often used in interfaces and most partners that I’ve worked with put every interface they ever did in their C/Side toolbox just in case you ever need it again.
Chances are you may never need many or most of them, but how do you determine that? And where to start?
If you can somehow decouple your business logic from your DotNET dependencies you create a Core solution, and each interface can be a dependent extension.
These depedent extensions will most often be per-tenant extensions. You don’t want to go through the whole AppSource validation for interfaces you only need almost never.
Let’s look at a simple real life example I did today. It’s an interface that reads the ferry sailing times from TT-Line and offers the option to book one.
Off course decoupling is smart here in the first place since you may also want to interface with other ferry companies but that’s not what we’re looking at now. In the example I have the C/AL code and the DotNET code is on one codeunit and it needs to be decoupled.
The Function GetTimeTable is called from the application somewhere. If I remove this codeunit from my extension I get errors.
Step 1 – Copy the codeunit using File-Save As and call it <<Name>> Impl.
This separates the link between your application and the code making the orriginal codeunit a Facade and the new codeunit the Implementation.
Microsoft also does this in the new System Application
Step 2 – Remove All Code from the Facade and change the functions in Event Publishers
This is different from what Microsoft does in System Application. In our case we don’t want a strong contract between the Facade and the Implementation.
Step 3 – Change the functions in the Implementation to event subscribers
Just enter the property window and add the correct properties. Trust me, it works.
Step 4 – Mark the decoupled codeunit in the Version List
When 100% conversion between C/Side and AL is the goal (and it should be the goal) you can use the Version List to mark your objects as part of Core, or as part of a dependent Extension.
Step 5 – Determine what’s next
Now you can ship to AppSource but you’ll have a function that does nothing. You need to rewrite this code to make it work.
Most often there are two scenario’s.
A:> Make use of the new HTTP type variables in AL
B:> Create an Azure Function
Both are valid options and the correct choice depends more on questions like flexibility and language of preference.
NEVER, EVER put interfaces in your Base AppSource solution unless you trust the contract of the API with your life!
Interfaces change all the time. Put them in a Per-Tenant extension or in an Azure Function because these can very easily be changed, debugged, etc.