Business Central Extensions: How AL Extensions Actually Work
A Business Central extension is a compiled, signed AL package that adds or modifies functionality without touching the base application's source code. There are three scopes: a Per-Tenant Extension (PTE) is bespoke to one customer environment and uses object IDs 50,000-99,999; an AppSource app is distributed through Microsoft's marketplace and uses a Microsoft-allocated range typically starting at 70,000,000; a DEV extension lives only in a sandbox. The AL code is nearly identical across scopes, but the object ID range, distribution channel, runtime architecture, and the upgrade and support model diverge completely. Written by a partner that implements both Dynamics 365 and Odoo for SMEs across Canada, the UK, and the US.
TL;DR — Key takeaways
- A Business Central extension is an AL-language package that adds or modifies functionality without touching the base application's source code.
- Development happens in Visual Studio Code with the official AL Language extension installed.
- Business Central supports three extension scopes, and choosing the right one is one of the most consequential decisions in any BC customization project.
- A common misconception is that PTEs and AppSource apps are technically different beasts.
What Are Business Central Extensions?
A Business Central extension is an AL-language package that adds or modifies functionality without touching the base application's source code. Since Microsoft moved the product away from C/AL and C/SIDE - completing the shift at Business Central Wave 2 (version 15) - the product is fully extension-based. Even the base application itself ships as an extension, alongside the open-source System Application modules that partners can consume.
Extensions are written in AL (Application Language), Microsoft's only supported language for Business Central development. They use constructs like tableextension, pageextension, and enumextension - plus event subscriptions and interfaces - to extend base objects rather than overwrite them. This extension-only architecture is what lets Microsoft ship twice-yearly major release waves without partners re-merging customizations into the core - the exact problem that made the old C/SIDE base-code modifications so expensive to maintain.
Under the hood, when a package is published to a Business Central environment, the AL compiler generates C# assemblies and metadata that are stored in the Azure SQL database and loaded into a runtime cache so objects start faster on subsequent runs. This compile step happens at installation time against the latest dependencies, which is why a correctly built .app file can still behave differently from one environment to the next if the platform version differs.
If you are weighing BC against a modular platform like Odoo (where Odoo Studio customizations layer on top of the core in a similar no-touch way), the parallel is real but the mechanics differ: BC enforces a compiled, signed, version-bound package model, while Odoo's customizations sit inside the app's own inheritance chain. Either way, the strategic lesson for SMEs is identical - extend, do not modify the vendor's base code.
How AL Extensions Work
Development happens in Visual Studio Code with the official AL Language extension installed. The AL: Go! command scaffolds a new project against a sandbox tenant, generating the app.json manifest, launch.json, and a sample HelloWorld page - enough to publish to a sandbox on the first run.
An extension's identity lives in app.json: a unique app ID (GUID), a version number, dependencies on other extensions (including the base application and the System Application), and the object ID range assigned to the publisher. Microsoft's documented object ranges reserve 0-49,999 for the Business Central base application - it must never be used in extensions - while 50,000-99,999 is the range for customer and partner customizations (the classic PTE range). Higher reserved ranges are allocated by Microsoft to AppSource publishers, typically in the 70,000,000+ block.
That object ID range is not a cosmetic convention - it is what distinguishes the extension types at the platform level. As practitioners put it, apps built in the 70,000,000 range are AppSource apps, while apps built in the 50,000 range are Per-Tenant Extensions. The two look similar in AL, but the range dictates where the app can be deployed and how it is governed.
Inside the package, AL enforces a clean structure: one object per .al file, a consistent prefix or suffix naming convention to avoid collisions, namespaces, PascalCase variables, 4-space indentation, and lowercase keywords. Microsoft's CodeCop and AppSourceCop analyzers enforce these conventions at compile time and flag issues before publish - AppSourceCop is mandatory for any app heading to AppSource validation, and PerTenantExtensionCop is the equivalent gatekeeper for PTEs, reminding developers to ship matching permission sets for every new table.
Microsoft's developer documentation defines extension identity precisely by scope. A Global (AppSource) app is uniquely defined by its id and version, and you cannot deploy a Global app with the same id and version but different content to multiple environments. A PTE adds a third dimension - it is uniquely defined by its app id, version, and the environment it is deployed to - which is why the same app id and version can legitimately exist with different content in two separate environments.
Extension Types: PTE vs AppSource vs DEV
Business Central supports three extension scopes, and choosing the right one is one of the most consequential decisions in any BC customization project. Microsoft's developer documentation defines them by scope: Global, Per-Tenant (PTE), and Dev.
Global apps are published by Microsoft or via AppSource and are available across tenants. Per-Tenant Extensions (PTEs) are uploaded directly to a specific environment and exist only there - their metadata and assemblies are not shared across environments, though a PTE can depend on Global apps, other PTEs, or DEV extensions. DEV extensions live only in sandbox environments and are uninstalled when the sandbox is updated or relocated, which is why they are strictly for development and throwaway exploration.
The dependency rules are scope-bound and strict: a Global app can only depend on other Global apps, whereas a PTE or DEV extension can depend on Global apps, other PTEs, or DEV extensions. You control the full lifecycle (publish, install, upgrade, uninstall) of PTEs and DEV extensions and can force-sync them; for Global apps you can install, uninstall, or upgrade but you do not control when they are published or unpublished, because other environments in the service may rely on them.
The simplest framing for an SME: if the customization would be valuable to many customers, build an AppSource app; if it is bespoke to one customer's processes, build a PTE; if it is throwaway exploration in a sandbox, use a DEV extension.
| Dimension | Per-Tenant Extension (PTE) | AppSource app |
|---|---|---|
| Audience | A single customer environment | Many customers via AppSource |
| Object ID range | 50,000-99,999 (customer/partner range) | Microsoft-allocated reserved range (typically 70,000,000+) |
| Distribution | Uploaded directly to the tenant (.app file) | Published and marketed through AppSource |
| Runtime architecture | Assemblies and metadata duplicated per environment | Assemblies and metadata shared across all installed tenants |
| Dependency rule | Can depend on Global apps, PTEs, or DEV extensions | Can only depend on other Global apps |
| Affix requirement | Not required | Required on all object names |
| Lifecycle control | Full - you publish, sync, upgrade, uninstall | Limited - install/uninstall/upgrade only; no forced sync |
| Pricing | Negotiated directly between partner and customer | Free, free-trial, or transactable (per-user monthly/yearly) |
| Upgrade cadence | You ship compatible versions on your schedule | Auto-updated to latest version at each major release wave |
| Go-to-market effort | Low - partner uploads the .app | High - Partner Center plans, entitlements, Microsoft validation |
PTE vs AppSource: The Decision Framework
A common misconception is that PTEs and AppSource apps are technically different beasts. They are not: both are written in AL, compiled to a .app file, and scaffolded the same way in Visual Studio Code. The technical work is almost identical; what diverges completely is the application lifecycle management, the support model, and the business case.
Choose a PTE when the customization is bespoke to one customer's processes, when speed to a single tenant matters more than market reach, and when the partner wants to retain direct control of the release schedule. PTEs carry no AppSource validation overhead and no marketing requirements - the partner compiles and uploads the .app and owns every upgrade.
Choose an AppSource app when the same functionality would be valuable to many customers, when recurring per-user revenue is the goal, or when the customization needs to run across multiple tenants in a single environment. AppSource apps also share their compiled assemblies and metadata across environments, which keeps memory usage lower and object load times faster than a PTE deployed many times - an important consideration in high-transaction environments.
The runtime difference is real even though it is invisible in the AL code. AppSource apps share their C# assemblies and metadata across every environment they are installed in, because they carry a single global identity granted by AppSource. A PTE does not share them - its assemblies and metadata are specific to each environment, so if you install the same PTE into two environments within a tenant, the metadata and assemblies are duplicated. Microsoft's guidance is direct: if you plan to distribute the same extension across different environments and tenants to multiple customers, distributing it via AppSource is usually the better choice.
| Signal | Lean toward a PTE | Lean toward an AppSource app |
|---|---|---|
| Audience | One customer, bespoke process | Many customers share the need |
| Revenue model | One-off project fee from the customer | Recurring per-user subscription |
| Distribution ambition | Single tenant, fast turnaround | Marketplace reach and discoverability |
| Validation appetite | None - partner controls releases | Willing to pass Microsoft technical + marketing validation |
| Multi-tenant footprint | Low - one or two environments | High - shared assemblies keep memory efficient |
The AL Extension Development Lifecycle
A typical PTE engagement moves through a predictable loop. The partner scaffolds the project in VS Code, develops against a sandbox, tests, and then publishes the compiled .app file to the customer's production environment via the Extension Management page or, since 2024 release wave 2, through the Business Central admin center.
On first install, an optional Install codeunit (SubType = Install) runs two triggers: OnInstallAppPerDatabase fires once per database, and OnInstallAppPerCompany fires once per company. These triggers seed default data, set up configuration, or migrate entries from a legacy table. Install codeunits run on first install and on reinstall - they do not run on a version upgrade, which is handled by a separate Upgrade codeunit.
That install-versus-reinstall distinction trips up many first-time PTE developers. Because the install triggers fire again whenever an app is uninstalled and reinstalled, naively seeding default configuration in OnInstallAppPerCompany will overwrite a customer's edits on the next reinstall. The reliable pattern is to call NavApp.GetCurrentModuleInfo and check whether DataVersion equals Version.Create(0,0,0,0) - if it does, it is a true fresh install and defaults are safe to populate; otherwise treat it as a reinstall and leave existing data alone.
- 01Scaffold and develop
Run AL: Go! in VS Code against a sandbox, write table/page/enum extensions and event subscribers, and validate with CodeCop and AppSourceCop (or PerTenantExtensionCop for a PTE).
- 02Match the runtime version
Ensure the runtime version in app.json is less than or equal to the platform version of the target tenant; download fresh symbols from the online tenant and confirm the platform version in System Information before building the .app.
- 03Test in sandbox
Publish as a DEV extension to a sandbox, exercise the new behavior, and confirm no regressions against the base objects you extended.
- 04Package the .app
Compile the project to a signed .app file with the correct app ID, version, and assigned object ID range recorded in app.json - and always increment the version before uploading, or the tenant rejects it as a duplicate.
- 05Install or deploy
Upload the .app to the customer's environment via Extension Management or the admin center (PTE), or submit to AppSource for Microsoft validation (Global app).
- 06Maintain and upgrade
Ship new versions with an Upgrade codeunit that migrates data and schema between versions, using upgrade tags to keep each step idempotent.
Dependencies and Moving Between Scopes
Dependencies are how BC composes functionality, and the rules are scope-bound. A PTE can take a dependency on an AppSource (Global) app - this is common when a bespoke customization builds on top of a purchased marketplace app. To compile such an extension, the developer adds the dependency in app.json using the AppSource app's id, name, publisher, and version, and Microsoft has progressively removed the friction of obtaining the dependent apps for development and testing.
Moving an extension from one scope to another is supported, but it is never a clean rename. Microsoft documents several migration paths, and each carries real consequences. Moving a PTE to the Marketplace is the most common ambition: a partner proves a customization with a few customers as a PTE, then productizes it as an AppSource app. The hard requirements are that you cannot have a Marketplace app and a PTE with the same app id, so you must change the id - which means the installed PTE's data is not automatically available to the new Marketplace app. You must also change every object's ID (PTEs live in the 50,000 range, Marketplace apps in their allocated range) and rename objects to use the affixes that Marketplace apps require but PTEs do not.
Because the data does not follow the new id, Microsoft's documented approach for preserving it is to run the PTE and the Marketplace app side by side - they must not share object names or IDs - and define a third PTE that depends on both and is responsible for migrating the data across. Note that even if you discontinue the Marketplace offer entirely, the app id remains reserved in Business Central and cannot be reused for a PTE later, so scope changes are effectively one-way at the identity level.
The reverse - narrowing a Marketplace app down to a PTE for select customers - follows the same identity and object-ID constraints. The practical takeaway for SMEs is to decide the scope early: building bespoke work as a PTE and later trying to productize it into an AppSource app is doable, but it is a migration project with data-handling work, not a settings toggle.
Upgrade Safety: Why Compatibility Matters
Business Central ships twice-yearly major release waves, and every installed extension must stay compatible with them. Microsoft's monthly cumulative updates do not introduce breaking changes, so extensions auto-uplift cleanly between minor versions. The major versions, every six months, are where extensions can break, and that is where the mandatory maintenance commitment bites.
If a publisher lets an extension go incompatible and does not ship a compatible version in time, Microsoft enforces a strict upgrade window rather than allowing indefinite postponement. The clock starts when the new major version is available: for roughly the first 30 days Microsoft alerts administrators, resellers, and ISVs until the upgrade succeeds; if upgrade attempts keep failing into the second month, Microsoft escalates to the customer, and for AppSource extensions that have not been made compatible within that window the app can be removed from AppSource. If incompatibility is still unresolved around day 60, Microsoft initiates a wind-down period that can culminate in automatically uninstalling the extension to keep the tenant operational.
Version-to-version migration is handled by an Upgrade codeunit (SubType = Upgrade). Microsoft's developer documentation defines its triggers: OnCheckPreconditions (and its PerDatabase/PerCompany variants) to fail fast if the environment is not ready; OnUpgradePerDatabase and OnUpgradePerCompany to perform the actual data and schema migration; and OnValidateUpgrade (with its PerDatabase/PerCompany variants) to assert the result is sound - an error here rolls the upgrade back.
Upgrade tags - recorded in a per-tenant upgrade tag table - are the recommended way to make each migration step idempotent, so a re-run does not double-apply data changes. The operational lesson is simple but easy to underestimate: every extension you install is a long-term maintenance commitment, not a one-time deployment. Either the publisher keeps it compatible with each major release wave inside that window, or the customer eventually loses upgrade headroom and risks a forced uninstall.
Managing Extensions: Install, Upload, Uninstall
Administrators manage extensions from the Extension Management page inside Business Central. From there they can install AppSource apps, upload and deploy PTE .app files, and uninstall extensions when no longer needed.
Since 2024 release wave 2, administrators can also manage Per-Tenant Extensions through the Business Central admin center and its API - the same surface already used to manage AppSource apps. This unifies PTE and AppSource operations and gives partners a cleaner remote-management story across environments.
Uninstalling an extension retains its data by default; the extension can be reinstalled later and the data is still there. To purge the data, an admin must explicitly choose to delete it - via clean-mode sync through the Administration Shell, or through the Delete Orphaned Extension Data page in the client. Dependents must be uninstalled first, which protects against accidentally orphaning another extension that relies on the one being removed.
Common PTE Pitfalls and Best Practices
Most PTE problems are not exotic; they come from a handful of habits that compound across release waves. The first is overusing the in-client Designer. Every time you save a change from Design mode, Business Central creates a separate extension, and if you then make a further change it creates a second extension that depends on the first. A few iterations in and you have an unmanageable dependency chain: upgrading the base extension forces you to uninstall the dependent one, upload the new version, and reinstall the dependent manually. The fix is to avoid Design mode for production PTEs - use F6 (Publish and open in Designer) from VS Code connected to the online tenant, which keeps everything inside one extension.
The second pitfall is a runtime version mismatch. The runtime version declared in app.json must be less than or equal to the platform version of the tenant you are uploading to. A PTE built against a newer runtime will upload and even build locally, then fail at install with an obscure object-reference error. Download fresh symbols from the target online tenant and confirm the platform version in System Information before packaging.
The third is missing permission sets. Uploading a PTE that adds tables without a matching permission set fails with a clear but easily forgotten error. Activating the PerTenantExtensionCop analyzer catches this at compile time, and the AL: Generate permission set containing current extension objects command generates the file for you.
The fourth is treating one PTE as reusable across many tenants. The same app id and version cannot be uploaded to different tenants within the same regional service without an error, because a PTE's identity is scoped to its environment. If a customization truly serves multiple customers, that is precisely the signal to productize it as an AppSource app rather than copy a PTE around. The discipline that ties all of this together is simple: the fewest, most deliberate extensions per tenant, each with clean versioning, matching permission sets, and an upgrade-tag strategy from version one.
How Flectic Approaches BC Extensions
Flectic implements Business Central (and Odoo) for SMEs across Canada, the UK, and the US. On BC projects we default to the fewest, most disciplined extensions possible - because every extension is a long-term upgrade commitment bounded by Microsoft's compatibility window, and the partners who over-extend are the ones whose customers lose upgrade headroom two release waves later.
Our AI-Assisted Delivery model is designed to deliver up to 3x faster than a traditional partner engagement on suitably scoped work - not unconditionally, and never as a substitute for the prerequisites that actually de-risk a BC project: a clean RapidStart data load, a documented extension inventory, and an upgrade-tag discipline from version one. If your current BC environment has accumulated years of ad-hoc PTEs or tangled Designer-mode dependency chains, an Extension Readiness review is usually the highest-value first step.
Frequently asked questions
What is the difference between a PTE and an AppSource app in Business Central?
A Per-Tenant Extension (PTE) is uploaded directly to one specific customer environment and exists only there, using the 50,000-99,999 object ID range. An AppSource app is published through Microsoft's marketplace, available across tenants, and uses a Microsoft-allocated reserved object ID range (typically 70,000,000+). PTEs duplicate their assemblies per environment; AppSource apps share them across tenants. PTEs are for bespoke, single-customer customizations; AppSource apps are for functionality many customers would pay for.
What is a DEV extension in Business Central?
A DEV extension lives only in a sandbox environment and is used for development and throwaway exploration. It is uninstalled automatically when the sandbox is updated or relocated, so it should never be used for production customizations. PTEs and Global (AppSource) apps are the two scopes that can run in production.
Can a PTE depend on an AppSource app?
Yes. A PTE can take a dependency on a Global (AppSource) app, another PTE, or a DEV extension. The developer declares the dependency in app.json using the AppSource app's id, name, publisher, and version. The reverse is not allowed: a Global app can only depend on other Global apps.
Do Business Central extensions run on version upgrades?
No. Install codeunits (SubType = Install) run OnInstallAppPerDatabase and OnInstallAppPerCompany triggers on first install and reinstall only. A version upgrade is handled by a separate Upgrade codeunit (SubType = Upgrade) with OnCheckPreconditions, OnUpgradePerDatabase/PerCompany, and OnValidateUpgrade triggers, made idempotent with upgrade tags.
How much time do you have to make an extension compatible with a new Business Central release?
Microsoft enforces a roughly 60-day window that starts when the new major version is available. For the first 30 days Microsoft alerts administrators, resellers, and ISVs until the upgrade succeeds; if it keeps failing into the second month, Microsoft escalates to the customer and can remove an AppSource app from the marketplace. If incompatibility is still unresolved around day 60, Microsoft initiates a wind-down period that can culminate in automatically uninstalling the extension to keep the tenant operational.
Can Microsoft automatically uninstall a Business Central extension?
Yes. If an extension is incompatible with an upcoming major release wave and the publisher does not ship a compatible version within the grace period, Microsoft can automatically uninstall the extension to keep the tenant operational. This is why every extension is a long-term compatibility commitment, not a one-time deployment.
What happens to data when you uninstall a Business Central extension?
Uninstalling an extension retains its data by default - the extension can be reinstalled and the data is still there. To remove the data, an admin must explicitly delete it via clean-mode sync or the Delete Orphaned Extension Data page. Dependents must be uninstalled first, which prevents orphaning another extension that depends on it.
Can you move a PTE to AppSource later?
You can, but it is a migration, not a toggle. You cannot have a Marketplace app and a PTE with the same app id, so you must change the id - which means the PTE's data does not carry over automatically. You must also move every object into the AppSource ID range and add the affixes Marketplace apps require. To preserve data, the documented approach is to run both apps side by side and use a third PTE depending on both to migrate the data.
How are Business Central extensions different from Odoo Studio customizations?
Both layer on top of the vendor's base application without modifying core source code, but the mechanics differ. BC extensions are compiled, signed, version-bound AL packages with enforced object ID ranges and a formal install/upgrade codeunit lifecycle. Odoo Studio customizations sit inside Odoo's own inheritance chain and are managed in-app. Flectic implements both and recommends the fewest, most disciplined customizations on either platform.
Sources & methodology
17 citedEvery pricing figure and statistic on this page is traced to a primary or vendor source with a verification date. Where partner pages are cited, their platform bias is disclosed in-line.
- 01Business Central object ID ranges: 0-49,999 reserved for the base application (must not be used in extensions); 50,000-99,999 for customer/partner customizations (PTEs); higher ranges allocated to AppSource publishers.↗learn.microsoft.com · verified 2026-06
- 02Business Central defines three extension scopes: Global (AppSource, cross-tenant), Per-Tenant (PTE, single environment), and Dev (sandbox only, removed on update). A PTE is uniquely defined by app id, version, and environment, and can depend on Global apps, other PTEs, or DEV extensions; a Global app can only depend on other Global apps.↗learn.microsoft.com · verified 2026-07
- 03An Install codeunit has SubType = Install and supports two triggers: OnInstallAppPerDatabase (runs once per database) and OnInstallAppPerCompany (runs for each company), fired on install or reinstall - not on version upgrade.↗learn.microsoft.com · verified 2026-06
- 04Upgrading extensions is handled by an Upgrade codeunit (SubType = Upgrade) with triggers OnCheckPreconditions, OnUpgradePerDatabase/OnUpgradePerCompany, and OnValidateUpgrade; upgrade tags make migration steps idempotent.↗learn.microsoft.com · verified 2026-06
- 05Since 2024 release wave 2, administrators can manage Per-Tenant Extensions through the Business Central admin center and its API, the same surface used to manage AppSource apps.↗learn.microsoft.com · verified 2026-06
- 06Uninstalling an extension retains its data by default; deleting data requires explicit clean-mode sync or the Delete Orphaned Extension Data operation; dependent extensions must be uninstalled first.↗learn.microsoft.com · verified 2026-06
- 07If an extension remains incompatible with a major update after the grace period, Microsoft can automatically uninstall it to keep the tenant operational.↗community.dynamics.com · verified 2026-06
- 08The shift from C/AL and C/SIDE to the extension-only AL model was completed at Business Central Wave 2 (version 15); the base application and System Application now ship as extensions.↗learn.microsoft.com · verified 2026-06
- 09When an app is published, the AL compiler generates C# assemblies and metadata stored in the Azure SQL database and loaded into a runtime cache. AppSource apps share assemblies and metadata across environments via global identity, while PTEs duplicate them per environment - so distributing the same extension across many tenants is usually better done via AppSource.↗demiliani.com · verified 2026-07
- 10Apps built in the 70,000,000 range are AppSource apps, while apps built in the 50,000 range are Per-Tenant Extensions; the object ID range is what distinguishes the extension types at the platform level.↗simplanova.com · verified 2026-07
- 11PTE best practices: avoid in-client Design mode (each save creates a separate dependent extension); ensure the runtime version in app.json is <= the tenant platform version; ship matching permission sets (enforced by PerTenantExtensionCop); the same app id and version cannot be uploaded to different tenants in the same regional service.↗simplanova.com · verified 2026-07
- 12Distinguish a fresh install from a reinstall in an Install codeunit by calling NavApp.GetCurrentModuleInfo and checking whether DataVersion equals Version.Create(0,0,0,0), so default configuration is not overwritten on reinstall.↗simplanova.com · verified 2026-07
- 13A PTE can take an AL dependency on an AppSource (Global) app by declaring the dependency in app.json with the app's id, name, publisher, and version.↗hougaard.com · verified 2026-07
- 14Moving a PTE to Marketplace requires changing the app id (no Marketplace app and PTE with the same id), moving objects into the AppSource ID range, and adding affixes Marketplace apps require; data is preserved by running both apps side by side and using a third PTE to migrate data. Even after discontinuing a Marketplace offer, the app id remains reserved and cannot be reused for a PTE.↗learn.microsoft.com · verified 2026-07
- 15Microsoft enforces a roughly 60-day window to make extensions compatible with a new major release: alerts to admins/resellers/ISVs in the first ~30 days, escalation to the customer and potential removal of the AppSource listing if still failing into the second month, and a wind-down period if unresolved around day 60.↗companial.com · verified 2026-07
- 16Marketplace apps auto-update to the latest version during upgrades to a new Business Central major version (e.g. 19.5 to 20.0) but are preserved during minor version upgrades (e.g. 19.0 to 19.1); monthly cumulative updates introduce no breaking changes.↗learn.microsoft.com · verified 2026-07
- 17The Lifecycle of apps and extensions FAQ documents how to upload new app versions to an existing Partner Center offer and deploy them via the Extension Management page in the Business Central web client.↗learn.microsoft.com · verified 2026-07
Related services & solutions
Planning a Business Central customization or extension project?
Flectic implements Business Central (and Odoo) for SMEs across Canada, the UK, and the US - platform-neutral advice, Success by Design methodology, and AI-Assisted delivery designed to deliver up to 3x faster on suitably scoped work. Book an ERP Readiness Call and we will map your extension scope, object ID strategy, and upgrade-safety plan before any code is written.