One of the common recommendations from both Microsoft and all the experts in our community is to create smaller modular extensions designed with either a single purpose or with a single functional area. Within software development this is often called Modular Development/Architecture.
This has several advantages, compared to create large (monolithic) extensions. The smaller the extension (the module) is the more likely it is that we can the sell the same extension to multiple customers. It also makes maintenance and extendibility easier. At least in theory, as it does have a lot of potential issues.
If the extension has no dependency, then you are safe to assume that it will not break due to upgrades by Microsoft. But if it didn't have extend any existing functionality in Business Central, then we didn't really need to develop as an Extension in the first place. That means that all extensions can theoretically break when Microsoft releases the next major update.
With each additional dependency to existing functionality in Business Central, then this risk that your extension may break goes up. And the risk continues to go up when we start to extend our own extensions. Or when we start to use our extension in different contexts, with different parallel installed extension.
The break may happen in two ways. If some of our integration points to other functionality is no longer present, has been renamed or changed, then your extension could fail when compiling it against the new version. That is the best case. It would be worse if it didn't fail, but that the functionality in our extension would have functional conflicts with the new version.
And that goes for all integration points, no matter if that is to standard Business Central code or to other extensions, no matter if developed by us or another Microsoft partner/ISV.
There are a many design patterns, that we can use when we develop the extension, which can help us to lower the risk in certain areas. But no matter how smart we are, then we can not eliminate it this way.
Even building large automated tests suites would not be able to eliminate it completely. But it can get us very close. If we are developing the extension for AppSource, then this is already a requirement from Microsoft. I would go one step further and recommend that every extension, no matter if for one customer or many, then we should always have covering test codeunits for it.
Anything we write in Visual Studio Code with AL, may potentially break. And that doesn't really matter this AL code is in a test codeunit inside the same extension or in a dedicated test extension. If it is code then it can break, and the more code we have, the higher the risk is that it will break.
A test codeunit as any other code may fail both when compiling it and when running. And in the same way as other code, then it needs maintenance.
No matter how we look at it, then having test codeunits makes the total cost risk of our entire solution higher. The more code the higher the risk.
If we want to have high quality Business Central extensions, which we safely can reuse and sell to multiple customers, then this is one of the things we must accept. And remember that the cleaner your code is, the easier (read less risk of breaking anything) future updates will be.
The next issues with having an extension with dependency to the main extension is specific to Business Central and the way Visual Studio and the AL compiler works.
Whenever we have a dependency in an extension to another extension or the standard BC functionality for that matter, then we need to download the symbols for this extension. These symbols are saves as .App files in the .alpackages folder in VSCode.
The result is that each time we change functionality in the main app and want to check that our dependent app still works, then we must first re-download the .app file for the main app.
And that's not really the worst part. That is the fact that we currently need to unpublish and uninstall all dependent extensions, before we can publish the main app again.
Even if not practical, then this may work in a situation where you are able to "freeze" the main app, before working on the dependent app.
If we want to separate our test codeunits into a separate extension, which is a requirement for AppSource extensions, then it makes it, if not impossible, then very difficult and impractical to do any kind of Test Driven Development with Business Central.
The TDD process is first to write the test, see the test fails, write just enough code to make the test pass, then refactor and write the next test. This process is also known as the "Red – Green – Refactor" mantra that we want to repeat again and again.
For this to be possible the individual steps need to be possible in seconds. It should not be something we measure in minutes!
The only practical way I have found is to have the test codeunits in the same extension. This way we can almost eliminate the issue of having to unpublish and republish the test app all the time. It also makes IntelliSense work (without the having to download the symbol file) and allows a full compilation across the "two" extensions.
This can work in the development phase, but generally we do not want to have test codeunits in a production database, so we would have the split it in two when we do the deployment build.
While the above "work around" can be used, then it's not really a good solution. It should be possible to do without having to hack the system.
Ideally if we had two (or more) dependent extensions inside the same workspace in Visual Studio Code, then the AL compiler shouldn't have the depend on the .App symbol files. It should be able to use the actual source files. Technically this should be possible, as all information is already present inside VSCode.
The same goes when it comes the process of having to unpublishing and republishing of dependent extensions. The user story for doing this, especially from VSCode, needs to be improved.
My personal fear is that many partners/developers would end by stuffing too much functionality into one extension, instead of splitting it up. Simply because working with multiple extensions is too time consuming.
If I were Microsoft, then I would do anything I could to improve working with multiple extensions. And I would do it fast, before most BC developers find out how difficult it currently is. An old say is that it you need ten good experiences to make up for one bad. I'm sure when Microsoft starts to split up the current C/Side core app into separate extensions, then we are going to see a lot of breaking extensions. Just as we saw when codeunit 1 was removed and refactored in other codeunits etc. And while most of them may not require too much time to fix, then they are all count as bad extension experiences.
And if it could make Test Driven Development possible, without having to find complex work arounds, would also be a great win.
If you agree, that it is important to improve the way we work more efficient with dependent extensions in Business Central, then please go to Microsoft's Business Central Idea site and give my two suggestions a vote up:
Enable compile/IntelliSense for apps within same workspace
Automatic unpublish and publish from VSCode
I am rewriting our old big vertical from Nav 3.10 to BC 16.
Spliting the whole app into smaller one seemed to be the right way for a team to developp some part independently.
But we are facing a wall. The model is not :
BC App » Ext A » Ext B, where Ext B is only dependent of Ext A, but :
- BC App » Ext A
- BC App » Ext B
- Ext A «» Ext B
So Each of extensions is dependant of the BC App and Ext A & B are dependent each other
Developping inside a workspace containing the 2 extensions is ok, and the packages creation are also ok because all symbols are available within the workspace.
But publishing is impossible because Ext A use symbols (ie table) whick are not published and Ext B do the same !
Any idea ?
about "We have to start thinking in tests (given-then-when) already when we define our user story. Ask the users what it should do, then write your tests (which fails), then write your code and make your test pass." - based on my experience with NAV I disagree with sequence, but I don't want to wrote it here (to exclude flooding)
When you publish with the Powershell functions or uploads to the cloud, then you are not loosing any data. Only when you do it from VSCode. But schema changes are not allowed. And as for this happening from VSCode, then make sure your tests are data agnostic. Meaning create test data in the test.
In some of my extensions, I have created a "setup demo" function which is called by the install function. This way, whenever I hit F5 to debug, then I already have a full dataset for my manual tests.
As to writing a test suite for a small extension, then that's just as important as doing it for a big extension. If it does matter if the code works or not, then sure don't test it. If you created a new field in a page, then you would want to test that the field is actually there and that the users can see and edit the field.
We have to start thinking in tests (given-then-when) already when we define our user story. Ask the users what it should do, then write your tests (which fails), then write your code and make your test pass. And next time there is an update, just run the test and you know the extension still works. You don't have to guess.
Great post, Erik, thank you.
it is good to have possibility to split up Extension now, but as you mentioned, MS will do it for own code and I think God only know who they plan to do it.
+ if we need to subscribe to validate 1 field in both "sub-extension" it is not possible to set sequence for it
Additionally, I can see 1 more point - when we unpublish&upinstall the extension we lost all data and I don't know how to handle this case (lets imagine situation when we have "* Ledger" and we need to unpublish it.. where can I store data and how can I attach it back)? I think it is more important then test CU.
P.S. about test CU - for small extension, from my point of view, I don't see any demands to have test CU
And that's always the case with automated and non automated testing. You only know what you know.