The biggest addition to Business Central 2023 Wave 2 (BC23) is the support for namespaces in AL. In this video, I take a practical approach to namespaces and show how it works. Check it out:

In this video, Erik explores the long-awaited namespaces feature that arrived with Business Central 2023 Wave 2 (BC23 / AL version 12). He walks through what namespaces are, how to declare them, how the using directive works, and shares practical advice on how to structure your namespaces without over-engineering them.
Background: Why Namespaces Matter
A little over half a year before this video, Erik published a video titled “We Really Need Name Spaces” — and he’s happy to take full credit for Microsoft finally delivering the feature. Jokes aside, namespaces have been a long-requested addition to AL development.
Up until BC23, Business Central (and its predecessors — Dynamics NAV, Navision, etc.) operated in a single global namespace. This meant:
- You couldn’t create a table called Customer because that name was already taken by the base application.
- You couldn’t create a codeunit called Sales-Post for the same reason.
- AppSource apps required prefixes (company initials, abbreviations, etc.) to avoid naming collisions — and those prefixes sometimes leaked into places they shouldn’t be visible.
Namespaces solve this problem by allowing you to divide content into logical partitions, much like they work in C#, Java, and many other languages.
Getting Started: Setting Up Runtime 12
To use namespaces, your app must target runtime 12.0. When creating a new app with AL:Go!, make sure you set the runtime appropriately. Erik notes that at the time of recording, the tooling briefly offered runtime 13 as an option — that was a bug and should be ignored. Runtime 12 is the correct version for namespaces support.
Here’s the source app.json from the demo project — note that this one is still on an older runtime and doesn’t yet include namespace configuration:
{
"id": "56158416-cadb-42da-a963-77ad9482cde9",
"name": "namespaces",
"publisher": "Default publisher",
"version": "1.0.0.0",
"brief": "",
"description": "",
"privacyStatement": "",
"EULA": "",
"help": "",
"url": "",
"logo": "",
"dependencies": [],
"screenshots": [],
"platform": "1.0.0.0",
"application": "21.0.0.0",
"idRanges": [
{
"from": 50100,
"to": 50149
}
],
"resourceExposurePolicy": {
"allowDebugging": true,
"allowDownloadingSource": true,
"includeSourceInSymbolFile": true
},
"runtime": "10.0",
"features": [
"NoImplicitWith"
]
}
To enable namespaces, you’d update the runtime to "12.0" and begin adding namespace declarations to your AL files.
How Namespaces Work in AL
Declaring a Namespace
A namespace is declared at the very top of an AL file. It is not a new object type — it’s a declaration that specifies where your code lives logically.
namespace Hogart.Demos.Namespace;
Once you add a namespace declaration, the compiler immediately starts enforcing it. Any references to objects in other namespaces will no longer resolve automatically — the compiler won’t know what Customer List is unless you tell it where to find it.
The using Directive
To reference objects from other namespaces without fully qualifying them every time, you add using directives below your namespace declaration:
namespace Hogart.Demos.Namespace;
using Microsoft.Sales.Customer;
using Microsoft.Purchases.Posting;
This is very similar to how using works in C#. With the using directive in place, you can reference Customer List directly without the full path.
Fully Qualified Names
If you don’t want to add a using directive — or if there’s an ambiguity where two namespaces contain objects with the same name — you can use the fully qualified name:
Microsoft.Sales.Customer."Customer List"
VS Code helps with this: pressing Ctrl+. (quick fixes) on an unresolved name gives you two options:
- Fully qualify the reference inline
- Add a using directive at the top of the file
Erik notes that this ambiguity issue is common in C# as well. For example, when working with both JSON and XML libraries, you might encounter multiple types called Element. In those cases, it’s often better to fully qualify the few references you need rather than adding a using that pollutes your combined namespace scope.
The Fallback Mechanism
Currently, Business Central has a fallback mechanism: if an app hasn’t been declared as a namespaced app, everything is treated as if it’s in the global scope. However, Erik warns that this is “a disaster waiting to happen” — as more developers adopt namespaces, duplicates in the global namespace will become inevitable, and non-namespaced apps will likely break.
Existing Objects: A Simple Codeunit Example
Here’s the helper codeunit from the demo project — a simple object that would live in whatever namespace you declare:
codeunit 50139 "Helper Functions"
{
procedure Helpful()
begin
Message('You are wonderful, have a nice day!');
end;
}
With a namespace declaration added at the top, this codeunit’s fully qualified name would become something like Hogart.Demos.Namespace."Helper Functions".
Can You Create a Table Called “Customer”?
Yes! Erik demonstrates this live. Because the new table lives in the Hogart.Demos.Namespace namespace, it doesn’t conflict with the base application’s Microsoft.Sales.Customer.Customer table. The app deploys successfully and both customer tables coexist.
However, Erik notes an interesting gap: when looking up tables in certain UI surfaces (like configuration packages), there’s no namespace information displayed — just the table name and ID. This could be confusing and is something Microsoft will likely need to address.
Fields Are Still Global (Prefixes Aren’t Dead Yet)
An important limitation: while objects can now share names across namespaces, field names on table extensions are still global. If you try to add a field called Name to the Customer table via a table extension, the compiler will reject it because that field name already exists on the table.
tableextension 50100 "Customer Extension" extends Microsoft.Sales.Customer.Customer
{
fields
{
field(50100; "Name"; Text[500]) // This will NOT compile — "Name" already exists
{
}
}
}
So while namespaces free us from prefixing object names, field-level prefixes are still necessary for table extensions. This may change in the future, but for now, that’s the reality.
Erik’s Recommended Namespace Structure
Erik shares his personal approach to structuring namespaces, keeping it intentionally simple:
- Level 1: Company/identity — e.g.,
Hogart - Level 2: Category — e.g.,
Apps,Tools,Demos - Level 3: Product/app name — e.g.,
SimpleObjectDesigner,Compiler
For example:
Hogart.Apps.SimpleObjectDesigner— a marketed applicationHogart.Tools.Compiler— a library/toolset used internallyHogart.Demos.Namespace— demo/learning code
He explicitly advises against adding a fourth level unless your project truly demands it. Most apps don’t need deep namespace hierarchies.
Open Questions
Erik highlights a few things that remain unclear or are still evolving:
- Existing app migration: What are the rules for an existing app or an existing AppSource app adopting namespaces?
- Namespace registration: Just as object prefixes are registered for AppSource today, there will likely need to be a registration system for top-level namespaces to prevent conflicts.
- UI surfaces: Several places in Business Central (like configuration packages and table lookups) don’t yet display namespace information, which can be confusing when objects share names.
Conclusion
Namespaces are a welcome and significant addition to AL development. They bring Business Central in line with modern programming language conventions and eliminate the awkward naming workarounds that developers have endured for decades. That said, Erik’s key piece of advice is worth repeating: don’t over-engineer it. You’ve lived in a single global namespace for years and managed just fine. Use namespaces logically, keep your structure simple, and resist the urge to create deeply nested hierarchies just because you can. As the Jurassic Park quote goes — be more concerned about whether you should than whether you could.