Merge pull request #258 from DmitryKatson/main

Vibe Coding for AL coding rules
This commit is contained in:
Arend-Jan Kauffmann 2025-08-08 15:31:12 +02:00 committed by GitHub
commit 92adcb9308
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1271 additions and 5 deletions

View file

@ -123,16 +123,16 @@ images: ["images/og-image-fission.png"]
</div>
<div class="col-lg-4">
<div class="card-shadow">
<span class="card-badge">PATTERN</span>
<span class="card-badge">NEW</span>
<h4 class="section-text-bold mt-4">
No. Series
Vibe Coding for AL
</h4>
<div class="card-shadow-content">
<p class="section-text">
The “Number Series” system is used extensively to provide numbers to master records, documents, and other transactions through Microsoft Dynamics 365 Business Central.
AI-optimized coding rules and guidelines designed to enhance the AL developer experience in modern AI-powered IDEs like VS Code and Cursor.
</p>
<a href="/docs/patterns/no-series/">
<button class="hero-mid-2 align-self-end">Read Now</button>
<a href="/docs/vibe-coding/">
<button class="hero-mid-2 align-self-end">Explore Rules</button>
</a>
</div>
</div>

View file

@ -0,0 +1,102 @@
# Vibe Coding Rules - Contribution Guide
This directory contains AI-optimized coding rules for AL development. Each rule set is organized into markdown files that can be easily consumed by AI coding assistants.
## Directory Structure
```
vibe-coding/
├── _index.md # Main landing page
├── README.md # This file - contribution guide
├── al-guidelines-rules.md # Complete rules file with references to all categories
├── al-code-style.md # Code style, formatting, and documentation rules
├── al-naming-conventions.md # File naming, object naming, and variable naming
├── al-performance.md # Performance optimization and query guidelines
├── al-error-handling.md # Error handling patterns and troubleshooting
├── al-events.md # Event-driven development and extensibility
├── al-testing.md # AL-Go workspace structure and testing guidelines
```
## How to Add New Rules
### 1. Choose the Right Category
Select the appropriate category for your rules, or create a new category if needed.
### 2. Follow the Standard Format
Each rule file should follow this structure:
```markdown
---
title: "AL [Category Name] Rules"
description: >
Brief description of the rule category for AL development
globs: ["*.al", "*.json"] # File types this rule applies to
alwaysApply: true|false # Whether to always apply these rules
---
# AL [Category Name] Rules
Brief introduction to the category and its importance in AL development.
## Rule 1: [Descriptive Title]
### Intent
What this rule aims to achieve, including implementation details and guidance for AI assistants.
### Examples
```al
// Good example
[code example]
```
You can also add a bad example to show what to avoid, but this is optional.
```al
// Bad example (avoid)
[code example]
```
## Rule 2: [Next Rule]
[Continue with same format...]
```
### 3. Update the Index
After adding new rule files:
- Update `al-guidelines-rules.md` to include `@your-new-file.md` reference
- Update `_index.md` to include links to new categories
- Ensure all cross-references are updated
### 4. Test Your Rules
Before submitting, test your rules with AI assistants to ensure they:
- Are clearly understood by AI agents
- Produce the expected code patterns
- Don't conflict with existing rules
## Contribution Workflow
1. **Fork** the alguidelines repository
2. **Create** your rule files in the appropriate category
3. **Test** the rules with your preferred AI assistant
4. **Submit** a pull request with:
- Clear description of the rules added
- Examples of how the rules improve code quality
- Any testing results with AI assistants (optional)
## Best Practices for Rule Writing
### Make Rules AI-Friendly
- Use clear, unambiguous language specific to AL development
- Provide specific AL code examples with proper syntax
- Include both positive and negative examples, when applicable
- Structure content consistently
## Questions?
If you have questions about contributing rules, please:
- Open a discussion in the GitHub repository
- Join the Business Central community Discord
- Contact the initiative maintainers
---
*This README is part of the Vibe Coding for AL initiative - enhancing AL development through AI-optimized guidelines.*

View file

@ -0,0 +1,100 @@
---
title: "Vibe Coding for AL"
tags: ["AL", "Vibe Coding"]
categories: ["Vibe Coding"]
description: >
AI-optimized coding rules and guidelines for AL development
---
_Created by the Business Central Community, Enhanced for AI-powered AL Development_
# Vibe Coding for AL
Welcome to the **Vibe Coding for AL** initiative! This section contains AI-optimized coding rules and guidelines specifically designed to enhance the AL developer experience in modern AI-powered IDEs like VS Code and Cursor.
## What is Vibe Coding?
Vibe Coding represents a new approach to coding guidelines that are specifically formatted and structured to work seamlessly with AI coding assistants. These rules are designed to:
- **Enhance AI Understanding**: Provide clear, structured guidelines that AI agents can easily parse and apply
- **Improve Code Quality**: Maintain high standards while leveraging AI assistance
- **Standardize Practices**: Create consistent coding patterns across the AL development community
- **Boost Productivity**: Help developers write better code faster with AI assistance
## Key Features
### 📋 **Structured Rule Format**
All rules are provided in markdown format with clear structure that AI agents can easily understand and apply during development.
### 🔄 **Community-Driven**
Built and maintained by the AL community, including MVPs and the Microsoft product team.
### 🛠️ **IDE Integration**
Future AL extension support will allow generating local rules files directly in VS Code and Cursor.
### 🤖 **AI-Ready**
Designed as a foundation for AL-focused AI tools and Model Context Protocols (MCPs).
## How to Use
1. **Browse the Rules**: Explore the various rule categories below
2. **Copy for Your Project**: Use these rules as templates for your own coding standards
3. **Contribute**: Submit your own rule variations via pull requests
4. **Stay Updated**: Watch for AL extension integration coming soon
## Rule Categories
The Vibe Coding rules are organized into logical categories to make them easy to find and implement:
- **[Complete AL Guidelines Rules](al-guidelines-rules/)** - Comprehensive rules file with references to other rules
- **[AL Code Style & Formatting](al-code-style/)** - Indentation, folder organization, and code documentation
- **[AL Naming Conventions](al-naming-conventions/)** - File naming, object naming, and variable naming patterns
- **[AL Performance Optimization](al-performance/)** - Query optimization, temporary tables, and performance analysis
- **[AL Error Handling & Troubleshooting](al-error-handling/)** - Try/catch patterns, debugging, and telemetry integration
- **[AL Event-Driven Development](al-events/)** - Event subscribers, integration events, and extensibility patterns
- **[AL Testing & Project Structure](al-testing/)** - AL-Go workspace structure, test generation, and project organization
## Getting Started
To get started with Vibe Coding for AL:
1. Review the rule categories that apply to your development needs
2. Adapt the rules to your specific project requirements
3. Configure your AI assistant to use these guidelines
4. Share your experiences and contribute improvements back to the community
## Future Roadmap
### Phase 1: Foundation ✅
- Host rules in AL Guidelines repository
- Community contribution process
- Initial rule sets from key contributors
### Phase 2: Integration 🔄
- AL extension support for local rules generation
- Enhanced AI agent compatibility
- MCP server integration
### Phase 3: Expansion 🚀
- Convert legacy C/AL patterns where applicable
- Generate new AL-specific patterns
- Establish as central trust source for AL AI agents
## Contributing
This initiative thrives on community contributions! Here's how you can help:
- **Submit Rule Sets**: Share your proven coding rules via pull requests
- **Improve Existing Rules**: Suggest enhancements to current guidelines
- **Test & Validate**: Try the rules in your projects and provide feedback
- **Share Examples**: Contribute real-world examples of rule applications
## Community & Support
- **GitHub Repository**: [Microsoft AL Guidelines](https://github.com/microsoft/alguidelines)
- **Discussions**: Join conversations about Vibe Coding rules
- **Issues**: Report problems or suggest new features
---
*The Vibe Coding for AL initiative is a collaborative effort between the Business Central community and Microsoft, aimed at revolutionizing how we write AL code in the age of AI.*

View file

@ -0,0 +1,167 @@
---
title: "AL Code Style & Formatting Rules"
description: >
AL Code structure, formatting, and folder organization guidelines for AL development
globs: ["*.al"]
alwaysApply: true
---
# AL Code Style & Formatting Rules
These rules ensure consistent code structure and organization across AL projects, making code more maintainable and AI-assistant friendly.
## Style guidelines for AL code
- Always use PascalCase for variable and function names.
- Use PascalCase for object names (e.g., tables, pages, reports).
- Maintain a consistent indentation style (2 spaces preferred).
## Commonly used methods and patterns
- Temporary tables for performance optimization
- Use of events for extensibility
## Rule 1: Consistent Indentation and Formatting
### Intent
Maintain consistent code formatting to improve readability and enable better AI understanding of code structure. Use indentation with two spaces consistently throughout your project and maintain consistent formatting within functions and procedures.
### Examples
```al
// Good example
procedure CalculateDiscount(Amount: Decimal; DiscountPct: Decimal): Decimal
begin
if DiscountPct > 0 then
exit(Amount * DiscountPct / 100);
exit(0);
end;
```
## Rule 2: Feature-Based Folder Organization
### Intent
Organize code by business features rather than object types to improve maintainability and logical grouping. Use feature-based organization with `src/feature/subfeature/` structure and place shared components in `Common` or `Shared` folders.
### Examples
```
// Good example - Feature-based organization
src/
├── NoSeries/
│ ├── NoSeries.Table.al
│ ├── NoSeries.Page.al
│ └── NoSeriesSetup.Codeunit.al
├── Sales/
│ ├── Invoice/
│ │ ├── SalesInvoice.Page.al
│ │ └── SalesInvoicePosting.Codeunit.al
│ └── Order/
│ └── SalesOrder.Page.al
└── Common/
├── Helpers/
│ └── DateHelper.Codeunit.al
└── Interfaces/
└── IPostable.Interface.al
```
```
// Bad example (avoid object-type segregation)
src/
├── Tables/
│ ├── NoSeries.Table.al
│ └── SalesHeader.Table.al
├── Pages/
│ ├── NoSeries.Page.al
│ └── SalesInvoice.Page.al
└── Codeunits/
├── NoSeriesSetup.Codeunit.al
└── SalesInvoicePosting.Codeunit.al
```
## Rule 3: Code Documentation and Comments
### Intent
Provide clear documentation for global functions using XML documentation comments. Code should be self-documenting through clear naming, but global functions in codeunits require proper documentation for API clarity.
### Examples
```al
// Good example - XML documentation for global functions
codeunit 50100 "Base64 Convert"
{
/// <summary>
/// Converts the value of the input string to its equivalent string representation that is encoded with base-64 digits.
/// </summary>
/// <param name="String">The string to convert.</param>
/// <returns>The string representation, in base-64, of the input string.</returns>
procedure ToBase64(String: Text): Text
begin
exit(Base64ConvertImpl.ToBase64(String));
end;
/// <summary>
/// Validates discount percentage against business rules.
/// </summary>
/// <param name="DiscountPct">The discount percentage to validate.</param>
procedure ValidateDiscountPercentage(DiscountPct: Decimal)
begin
if DiscountPct > 50 then
Error('Discount cannot exceed 50% due to company policy');
if DiscountPct < 0 then
Error('Discount percentage cannot be negative');
end;
}
```
```al
// Bad example (avoid inline comments for obvious operations)
procedure ValidateDiscountPercentage(DiscountPct: Decimal)
begin
// Check if discount is greater than 50
if DiscountPct > 50 then
Error('Discount cannot exceed 50%');
// Check if discount is less than 0
if DiscountPct < 0 then
Error('Discount percentage cannot be negative');
end;
```
## Rule 4: Modular and Reusable Code Structure
### Intent
Keep code modular and reusable to enhance maintainability and reduce duplication. Write small, focused procedures that do one thing well and use interfaces and patterns where appropriate.
### Examples
```al
// Good example - Modular approach
procedure PostDocument(var DocumentHeader: Record "Sales Header")
begin
ValidateDocument(DocumentHeader);
CalculateTotals(DocumentHeader);
CreateLedgerEntries(DocumentHeader);
UpdateStatus(DocumentHeader);
end;
local procedure ValidateDocument(var DocumentHeader: Record "Sales Header")
begin
if DocumentHeader."No." = '' then
Error('Document number cannot be empty');
end;
local procedure CalculateTotals(var DocumentHeader: Record "Sales Header")
begin
DocumentHeader.CalcFields(Amount);
end;
```
```al
// Bad example (avoid monolithic procedures)
procedure PostDocument(var DocumentHeader: Record "Sales Header")
begin
// All validation, calculation, and posting logic in one procedure
// ... 200+ lines of mixed concerns
end;
```

View file

@ -0,0 +1,167 @@
---
title: "AL Error Handling & Troubleshooting Rules"
description: >
AL Error handling patterns, debugging techniques, and troubleshooting guidelines for AL development
globs: ["*.al"]
alwaysApply: false
---
# AL Error Handling & Troubleshooting Rules
Robust error handling and effective troubleshooting practices are essential for maintaining reliable Business Central applications.
## Rule 1: Use TryFunctions for Error Handling
### Intent
Implement proper error handling using TryFunctions to manage exceptions gracefully and provide meaningful user feedback. Use TryFunctions for error handling in scenarios where rollback is required, implement proper exception handling for external service calls, provide meaningful error messages to users, and log errors appropriately for debugging purposes. When generating code that might fail (external calls, data operations, calculations), implement appropriate TryFunction error handling and provide clear error messages.
### Examples
```al
// Good example - TryFunction with proper error handling and error labels
procedure ProcessPayment(Amount: Decimal): Boolean
var
PaymentService: Codeunit "Payment Service";
ErrorText: Text;
PaymentProcessingFailedLbl: Label 'Payment processing failed: %1', Comment = '%1 = Error message';
PaymentProcessingFailedTelemetryLbl: Label 'Payment processing failed', Locked = true;
begin
if not TryProcessPaymentInternal(Amount) then begin
ErrorText := GetLastErrorText();
LogError(PaymentProcessingFailedTelemetryLbl, ErrorText);
Message(PaymentProcessingFailedLbl, ErrorText);
exit(false);
end;
exit(true);
end;
[TryFunction]
local procedure TryProcessPaymentInternal(Amount: Decimal)
var
PaymentService: Codeunit "Payment Service";
begin
PaymentService.ProcessPayment(Amount);
end;
```
```al
// Bad example (avoid hardcoded error messages and unhandled errors)
procedure ProcessPayment(Amount: Decimal)
var
PaymentService: Codeunit "Payment Service";
begin
// No error handling - will cause unhandled exceptions
// Also avoid hardcoded messages like this:
// Message('Payment could not be processed');
PaymentService.ProcessPayment(Amount);
end;
```
## Rule 2: Use Error Labels for All Messages
### Intent
All error messages, warnings, and user messages must use label variables instead of hardcoded text. This ensures proper localization support and maintainability. Define labels with appropriate comments for translators and use Locked = true for technical messages that should not be translated.
### Examples
```al
// Good example - Using error labels
procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header")
var
Customer: Record Customer;
CustomerNotFoundErr: Label 'Customer %1 does not exist for sales document %2.', Comment = '%1 = Customer No., %2 = Sales Header No.';
CustomerBlockedErr: Label 'Customer %1 is blocked (%2). Cannot process sales document %3.', Comment = '%1 = Customer No., %2 = Blocked reason, %3 = Sales Header No.';
EmptyHeaderNoErr: Label 'Sales header number cannot be empty.';
begin
if SalesHeader."No." = '' then
Error(EmptyHeaderNoErr);
if not Customer.Get(SalesHeader."Sell-to Customer No.") then
Error(CustomerNotFoundErr, SalesHeader."Sell-to Customer No.", SalesHeader."No.");
if Customer.Blocked <> Customer.Blocked::" " then
Error(CustomerBlockedErr, Customer."No.", Customer.Blocked, SalesHeader."No.");
end;
```
```al
// Bad example (avoid hardcoded error messages)
procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header")
var
Customer: Record Customer;
begin
if not Customer.Get(SalesHeader."Sell-to Customer No.") then
Error('Customer not found'); // Hardcoded - avoid this
if Customer.Blocked <> Customer.Blocked::" " then
Error('Customer blocked'); // Hardcoded - avoid this
end;
```
## Rule 3: Code Compilation and Correctness Priority
### Intent
Generated AL code should prioritize correctness over immediate compilation. Code can fail to compile if AI suggests base functions or events that don't exist, or if variables in event subscriptions are incorrect. When this happens, leave space for manual fixes rather than changing the intended behavior. If you're confident the logic should work as suggested but there are naming or parameter issues, leave it for user correction rather than altering the business logic.
### Examples
```al
// Good example - Correct logic even if function names need verification
procedure HandleCustomerModification(var Customer: Record Customer)
var
CustomerValidation: Codeunit "Customer Validation"; // May need verification
begin
// Correct business logic - even if codeunit name needs adjustment
if not CustomerValidation.ValidateCustomerData(Customer) then
Error(ValidationFailedErr);
Customer.Modify(true);
end;
```
```al
// Good example - Event subscription with correct intent
[EventSubscriber(ObjectType::Table, Database::Customer, OnAfterModifyEvent, '', false, false)]
local procedure OnAfterCustomerModify(var Rec: Record Customer; var xRec: Record Customer; RunTrigger: Boolean)
var
CustomerChangeLog: Codeunit "Customer Change Log"; // Function may need verification
begin
// Correct logic - even if codeunit or method names need adjustment
CustomerChangeLog.LogCustomerChange(Rec, xRec);
end;
```
## Rule 4: Custom Telemetry Implementation
### Intent
Add custom telemetry for tracking business-critical operations, but only when explicitly requested by the user. Use Session.LogMessage for custom telemetry with appropriate verbosity levels and data classification. Include relevant custom dimensions for context and use proper telemetry scope for extension publishers.
### Examples
```al
// Good example - Custom telemetry (only when user explicitly requests it)
procedure PostSalesDocument(var SalesHeader: Record "Sales Header")
var
TelemetryCustomDimensions: Dictionary of [Text, Text];
SalesDocPostedMsg: Label 'Sales document posted successfully', Locked = true;
SalesDocPostFailedMsg: Label 'Sales document posting failed', Locked = true;
begin
// Add context for telemetry
TelemetryCustomDimensions.Add('DocumentType', Format(SalesHeader."Document Type"));
TelemetryCustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No.");
if TryPostSalesDocument(SalesHeader) then begin
// Log successful operation
Session.LogMessage('SAL001', SalesDocPostedMsg,
Verbosity::Normal, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions);
end else begin
// Log failed operation with error details
TelemetryCustomDimensions.Add('ErrorText', GetLastErrorText());
Session.LogMessage('SAL002', SalesDocPostFailedMsg,
Verbosity::Error, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions);
end;
end;
```

View file

@ -0,0 +1,120 @@
---
title: "Event-Driven Development Rules"
description: >
Guidelines for implementing event-driven patterns and extensibility in AL development
globs: ["*.al"]
alwaysApply: false
---
# Event-Driven Development Rules
Event-driven development is fundamental to creating extensible and maintainable Business Central applications that follow the platform's architecture principles.
## Rule 1: Use Events for Extensibility
### Intent
Implement proper event patterns to enable extensibility without modifying base application code. Subscribe to relevant Business Central events (OnBeforeInsert, OnAfterModify, etc.), create integration events in your code for future extensibility, use extension objects or events for all changes to standard application objects. When implementing business logic, prioritize event subscribers and suggest appropriate event subscription patterns and integration event creation.
### Examples
```al
// Good example - Event subscriber implementation with Handler suffix
codeunit 50100 "Sales Document Events Handler"
{
[EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)]
local procedure OnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean)
begin
// Custom validation logic
ValidateCustomFields(SalesHeader);
end;
}
```
## Rule 2: Add Integration Events for Extensibility
### Intent
Use integration events to provide better extensibility points and clearer API contracts for other developers. Create integration events at logical business process points, document integration event parameters and expected behavior, provide meaningful event names that describe the business context, and implement handled patterns to allow subscribers to control execution flow. When designing extensible code, suggest integration events at appropriate business logic points with clear documentation and meaningful names.
### Examples
```al
// Good example - Integration events with handled pattern
codeunit 50101 "Customer Management"
{
procedure CreateCustomer(var Customer: Record Customer): Boolean
var
IsHandled: Boolean;
begin
OnBeforeCreateCustomer(Customer, IsHandled);
if IsHandled then
exit(true);
if not Customer.Insert(true) then
exit(false);
OnAfterCreateCustomer(Customer);
exit(true);
end;
[IntegrationEvent(false, false)]
procedure OnBeforeCreateCustomer(var Customer: Record Customer; var IsHandled: Boolean)
begin
// Allow extensions to modify customer data before creation
// Set IsHandled to true to skip default processing
end;
[IntegrationEvent(false, false)]
procedure OnAfterCreateCustomer(var Customer: Record Customer)
begin
// Allow extensions to perform additional actions after customer creation
end;
}
```
```al
// Extension subscribing to integration events with Handler suffix
codeunit 50102 "Customer Validation Handler"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Management", OnBeforeCreateCustomer, '', false, false)]
local procedure ValidateCustomerOnBeforeCreate(var Customer: Record Customer; var IsHandled: Boolean)
begin
// Custom validation logic
ValidateCustomerCreditLimit(Customer);
// Optionally handle the event to skip default processing
if ShouldSkipDefaultProcessing(Customer) then
IsHandled := true;
end;
}
```
## Rule 3: Event Parameter Best Practices
### Intent
Design event parameters that provide sufficient context while maintaining performance and usability. Pass record variables by reference when possible, include relevant context parameters, use meaningful parameter names, consider performance implications of parameter passing, and implement handled patterns where appropriate. When creating events, ensure parameters provide sufficient context for subscribers while maintaining good performance and use descriptive parameter names that clearly indicate their purpose.
### Examples
```al
// Good example - Well-designed event parameters with handled pattern
codeunit 50103 "Document Posting Events"
{
[IntegrationEvent(false, false)]
procedure OnBeforePostDocument(var DocumentHeader: Record "Sales Header"; var DocumentLines: Record "Sales Line"; PostingDate: Date; var IsHandled: Boolean)
begin
// Comprehensive context for document posting
// - Document header and lines for full context
// - Posting date for temporal context
// - IsHandled flag for control flow
end;
[IntegrationEvent(false, false)]
procedure OnAfterPostDocument(DocumentHeader: Record "Sales Header"; PostedDocumentNo: Code[20]; PostingResult: Boolean)
begin
// Results context after posting
// - Original document for reference
// - Posted document number for tracking
// - Success/failure indication
end;
}
```

View file

@ -0,0 +1,58 @@
---
description: AL Guidelines - Comprehensive AI-optimized coding rules for Microsoft Dynamics 365 Business Central development
globs: ["*.al", "*.json", "app.json", "launch.json"]
alwaysApply: true
---
# AL Guidelines - Vibe Coding Rules
You are an AI assistant designed to aid in AL development, particularly for Microsoft Dynamics 365 Business Central. Your role is to assist developers in writing efficient, maintainable code following established patterns and best practices.
## Core Principles
- Follow event-driven programming model; never modify standard application objects
- Use clear, meaningful names and maintain consistent code structure
- Prioritize performance optimization and proper error handling
- Focus on main application implementation by default
- Only generate test code when explicitly requested
- Maintain proper AL-Go workspace structure separation
## Rule Categories
The following rule sets provide comprehensive guidance for AL development:
@al-code-style.md
@al-naming-conventions.md
@al-performance.md
@al-error-handling.md
@al-events.md
@al-testing.md
## Key Guidelines Summary
- **File Naming**: Use `<ObjectName>.<ObjectType>.al` pattern consistently
- **Code Style**: Use two space indentation and PascalCase for variables, PascalCase for objects
- **Folder Structure**: Organize by feature (`src/feature/subfeature/`) not by object type
- **Performance**: Filter data early, use temporary tables, avoid unnecessary loops
- **Events**: Prefer integration events over direct modifications for extensibility
- **Testing**: Separate App and Test projects, generate tests only when requested
- **Error Handling**: Use TryFunctions, provide meaningful error messages, implement telemetry
## AL-Go Workspace Structure
When working in AL-Go environments:
- **App project**: Contains all application logic (tables, pages, codeunits, reports)
- **Test project**: Contains all test code and references App project as dependency
- **Never mix**: Application code stays in App, test code stays in Test project
## AI Response Behavior
- Provide concise, actionable advice with specific AL method references
- Always explain the reasoning behind recommendations
- Reference Business Central architecture patterns and established best practices
- Focus on practical implementation guidance that can be immediately applied

View file

@ -0,0 +1,133 @@
---
title: "Naming Conventions Rules"
description: >
Comprehensive naming conventions for AL files, objects, variables, and functions
globs: ["*.al"]
alwaysApply: true
---
# Naming Conventions Rules
Consistent naming conventions improve code readability, maintainability, and help AI assistants understand code structure and intent.
## Rule 1: Object Naming Conventions
### Intent
Use consistent naming patterns for all AL objects to improve discoverability and maintain professional standards. Use PascalCase for object names (tables, pages, reports, codeunits) and meaningful, descriptive names that clearly indicate the object's purpose. Object names must not exceed 30 characters total, with a maximum of 26 characters for the name itself to reserve space for prefixes/affixes (3 characters + 1 space).
### Examples
```al
// Good examples (within 26 character limit)
table 50100 "Customer Ledger Entry" // 20 chars
page 50101 "Sales Invoice" // 13 chars
codeunit 50102 "Sales Invoice Posting" // 21 chars
report 50103 "Customer Statement" // 18 chars
```
```al
// Bad examples (avoid abbreviations, unclear names, or length violations)
table 50100 "CustLE" // Too abbreviated
page 50101 "SalesInv" // Too abbreviated
table 50104 "Very Long Customer Ledger Entry" // 32 chars - exceeds limit
codeunit 50102 "SIPoster" // Unclear abbreviation
```
## Rule 2: File Naming Conventions
### Intent
Establish consistent file naming patterns that clearly identify object types and facilitate organized development. Use pattern `<ObjectName>.<ObjectType>.al` and maintain consistency across all file names. Ensure file names are descriptive and match the AL object name within the files.
### Examples
```al
// Good examples
NoSeries.Page.al
NoSeries.Table.al
NoSeriesErrorsImpl.Codeunit.al
NoSeriesSetup.Codeunit.al
CustomerCard.Page.al
SalesHeader.Table.al
PostSalesInvoice.Codeunit.al
ItemLedgerEntry.Report.al
InventorySetup.PageExt.al
SalesHeader.TableExt.al
// For implementations and interfaces
INoSeries.Interface.al
NoSeriesImpl.Codeunit.al
// For test files
NoSeriesTests.Codeunit.al
SalesPostingTests.Codeunit.al
```
## Rule 3: Variable and Function Naming
### Intent
Use consistent naming conventions for variables and functions to improve code readability. Use PascalCase for variable and function names, descriptive names that clearly indicate purpose, and avoid abbreviations unless they are well-known business terms. Use consistent parameter naming in procedures.
### Examples
```al
// Good examples - Variables
var
CustomerLedgerEntry: Record "Cust. Ledger Entry";
TotalAmount: Decimal;
DiscountPercentage: Decimal;
IsValidTransaction: Boolean;
```
```al
// Good examples - Functions
procedure CalculateCustomerBalance(CustomerNo: Code[20]): Decimal
procedure ValidateSalesDocument(var SalesHeader: Record "Sales Header")
procedure UpdateInventoryQuantity(ItemNo: Code[20]; Quantity: Decimal)
```
## Rule 4: Parameter Naming in Event Subscribers
### Intent
Use meaningful parameter names in event subscribers to improve code clarity and maintainability. Use descriptive parameter names that clearly indicate their purpose, follow Business Central conventions for common parameter types, and maintain consistency across similar event subscribers. Avoid unclear generic names like "Rec" - use specific descriptive names.
### Examples
```al
// Good example - Descriptive parameter names
[EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)]
local procedure AddDefaultValuesOnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean)
begin
// Event handling logic
end;
[EventSubscriber(ObjectType::Table, Database::Customer, OnBeforeModify, '', false, false)]
local procedure CheckBalanceOnBeforeModifyCustomer(var Customer: Record Customer; var xCustomer: Record Customer)
begin
// Event handling logic
end;
```
## Rule 5: Interface and Implementation Naming
### Intent
Clearly distinguish between interfaces and their implementations using consistent naming patterns. Prefix interfaces with "I" (e.g., `INoSeries`), use "Impl" suffix for implementation codeunits, and keep interface and implementation names closely related. Ensure names stay within the 26-character limit.
### Examples
```al
// Good examples (within character limits)
// Interface file: ICustomerService.Interface.al
interface ICustomerService
{
procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal;
}
// Implementation file: CustomerServiceImpl.Codeunit.al
codeunit 50100 "Customer Service Impl" implements ICustomerService
{
procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal
begin
// Implementation logic
end;
}
```

View file

@ -0,0 +1,225 @@
---
title: "AL Performance Optimization Rules"
description: >
Performance optimization guidelines and best practices for AL development
globs: ["*.al"]
alwaysApply: true
---
# AL Performance Optimization Rules
These rules focus on writing performant AL code that scales well and provides optimal user experience in Business Central environments.
## AL Performance Guidelines Summary
- Always analyze performance impact when adding new features
- Optimize queries by filtering data as early as possible
- Avoid unnecessary loops; use set-based operations when possible
- Use SetLoadFields to minimize data retrieval
- Use temporary tables, dictionaries, or lists for temporary data storage
## Rule 1: Early Data Filtering and Query Optimization
### Intent
Optimize queries by filtering data as early as possible to reduce data transfer and processing overhead. Apply filters before processing records, use appropriate table keys and sorting, minimize the amount of data retrieved from the database, and use SetRange and SetFilter methods effectively.
### Examples
```al
// Good example - Early filtering
procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer
var
Customer: Record Customer;
begin
Customer.SetRange(City, CityFilter);
Customer.SetRange(Blocked, Customer.Blocked::" ");
if Customer.FindSet() then
repeat
// Process only filtered customers
until Customer.Next() = 0;
exit(Customer.Count);
end;
```
```al
// Bad example (avoid processing all records)
procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer
var
Customer: Record Customer;
Count: Integer;
begin
if Customer.FindSet() then
repeat
// Processing all customers then filtering
if (Customer.City = CityFilter) and (Customer.Blocked = Customer.Blocked::" ") then
Count += 1;
until Customer.Next() = 0;
exit(Count);
end;
```
## Rule 2: Use SetLoadFields for Optimal Data Retrieval
### Intent
Use SetLoadFields to minimize data retrieval from the database by loading only the fields you need. Place SetLoadFields before the Get or Find operation, and include only the fields that will be used in your code.
### Examples
```al
// Good example - SetLoadFields with filtering
Item.SetRange("Third Party Item Exists", false);
Item.SetLoadFields("Item Category Code");
Item.FindFirst();
```
```al
// Bad example (avoid SetLoadFields after filtering)
Item.SetLoadFields("Item Category Code");
Item.SetRange("Third Party Item Exists", false);
Item.FindFirst();
```
## Rule 3: Use Temporary Tables, Dictionaries, and Lists for Performance
### Intent
Leverage temporary tables, dictionaries, and lists to improve performance in read-heavy scenarios and complex data processing. Use temporary tables for structured record data, dictionaries for key-value pairs, and lists for simple collections that are only temporarily needed.
### Examples
```al
// Good example - Using temporary tables for structured data
procedure ProcessSalesData(var TempSalesLine: Record "Sales Line" temporary)
var
SalesLine: Record "Sales Line";
begin
// Load data into temporary table once
if SalesLine.FindSet() then
repeat
TempSalesLine := SalesLine;
TempSalesLine.Insert();
until SalesLine.Next() = 0;
// Process temporary data multiple times without database hits
ProcessDiscounts(TempSalesLine);
CalculateTotals(TempSalesLine);
ValidateInventory(TempSalesLine);
end;
```
```al
// Good example - Using dictionaries for key-value temporary data
procedure CacheCustomerData()
var
Customer: Record Customer;
CustomerCache: Dictionary of [Code[20], Text];
begin
if Customer.FindSet() then
repeat
CustomerCache.Add(Customer."No.", Customer.Name);
until Customer.Next() = 0;
// Use cached data for lookups
ProcessOrdersWithCache(CustomerCache);
end;
```
```al
// Good example - Using lists for simple collections
procedure GetBlockedCustomers(): List of [Code[20]]
var
Customer: Record Customer;
BlockedCustomers: List of [Code[20]];
begin
Customer.SetRange(Blocked, Customer.Blocked::All);
if Customer.FindSet() then
repeat
BlockedCustomers.Add(Customer."No.");
until Customer.Next() = 0;
exit(BlockedCustomers);
end;
```
## Rule 4: Avoid Unnecessary Loops - Use Set-Based Operations
### Intent
Minimize looping operations and favor set-based approaches when possible to improve performance. Use built-in aggregation methods (CalcSums, CalcFields), leverage SQL-based operations through AL, avoid nested loops when possible, and use batch operations for multiple record updates.
### Examples
```al
// Good example - Set-based operation
procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal
var
CustLedgerEntry: Record "Cust. Ledger Entry";
begin
CustLedgerEntry.SetRange("Customer No.", CustomerNo);
CustLedgerEntry.CalcSums(Amount);
exit(CustLedgerEntry.Amount);
end;
```
```al
// Bad example (avoid manual loops for aggregation)
procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal
var
CustLedgerEntry: Record "Cust. Ledger Entry";
TotalAmount: Decimal;
begin
CustLedgerEntry.SetRange("Customer No.", CustomerNo);
if CustLedgerEntry.FindSet() then
repeat
TotalAmount += CustLedgerEntry.Amount;
until CustLedgerEntry.Next() = 0;
exit(TotalAmount);
end;
```
## Rule 5: Performance Impact Analysis
### Intent
Always analyze and consider performance impact when adding new features or modifying existing code. While the AL compiler does not have direct access to performance profilers, you should implement performance-optimal code patterns from the start and consider scalability implications of code changes.
### Examples
```al
// Good example - Performance-conscious implementation
procedure UpdatePricesForItems(var Item: Record Item)
var
ItemCount: Integer;
begin
// Check data volume before processing
ItemCount := Item.Count();
if ItemCount > 1000 then begin
// Use batch processing for large datasets
UpdatePricesInBatches(Item);
end else begin
// Direct processing for smaller datasets
UpdatePricesDirectly(Item);
end;
end;
```
```al
// Good example - Batched modifications to minimize database writes
procedure UpdateCustomerStatistics(CustomerNo: Code[20])
var
Customer: Record Customer;
TotalBalance: Decimal;
LastPaymentDate: Date;
begin
// Calculate all values first
CalculateCustomerTotals(CustomerNo, TotalBalance, LastPaymentDate);
// Single database write with all changes
Customer.SetLoadFields("Balance (LCY)", "Last Payment Date");
if Customer.Get(CustomerNo) then begin
Customer."Balance (LCY)" := TotalBalance;
Customer."Last Payment Date" := LastPaymentDate;
Customer.Modify(true);
end;
end;
```

View file

@ -0,0 +1,194 @@
---
title: "AL Testing & Project Structure Rules"
description: >
AL-Go workspace structure, test generation guidelines, and project organization rules
globs: ["*.al", "app.json"]
alwaysApply: false
---
# AL Testing & Project Structure Rules
These rules ensure proper project organization, test implementation, and workspace structure in AL-Go based development environments.
## Rule 1: AL-Go Workspace Structure Guidelines
### Intent
Establish clear separation between application code and test code in AL-Go workspace environments.
- App project contains Tables, Pages, Codeunits, Reports, APIs, Enums, etc.
- Test project contains Test Codeunits, Test Pages, Mock objects, Test data.
- Each project has its own app.json with appropriate dependencies.
- Test project references the App project as a dependency.
- Use App project ONLY for main application implementation, use Test project ONLY for test implementation, never include test files in the main App folder, and never include application logic in the Test folder.
- When working in AL-Go workspace, always place files in the correct project based on their purpose.
### Examples
```
// Good example - Proper AL-Go workspace structure
Repository/
├── .AL-Go/
├── .github/
├── App/
│ ├── src/
│ │ ├── Setup/
│ │ ├── Feature1/
│ │ ├── Feature2/
│ │ ├── APIs/
│ ├── app.json
│ └── launch.json
├── Test/
│ ├── src/
│ │ ├── SetupTests/
│ │ ├── Feature1Tests/
│ │ ├── Feature2Tests/
│ │ ├── IntegrationTests/
│ ├── app.json
│ └── launch.json
└── al.code-workspace
```
## Rule 2: Test Generation Guidelines
### Intent
Control when and how test code is generated to maintain focus on main application implementation.
- DO NOT automatically generate test code unless explicitly requested
- Focus on main application implementation by default
- When user asks for implementation create only the main application objects
- Only generate test files when user specifically requests "Create tests for...", "Generate unit tests...", "Add test coverage...", or "Write tests..."
- If tests are requested, place them in the Test project following the folder structure where test files should mirror the App project structure but in the Test project
- Unless the user explicitly requests tests, focus only on main application implementation
## Rule 3: Project Dependencies Configuration
### Intent
Establish correct dependency relationships between App and Test projects.
- App project app.json should NOT reference Test project
- Test project app.json MUST reference App project as dependency
- Test project should include testing frameworks (e.g., "Any", "Library Assert"), and each project maintains its own dependencies.
- When configuring project dependencies, ensure Test project references App project but never the reverse and include appropriate testing frameworks in Test project.
### Examples
```json
// Good example - Test project app.json
{
"dependencies": [
{
"id": "your-app-id",
"name": "Your App Name",
"publisher": "Your Publisher",
"version": "1.0.0.0"
},
{
"id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14",
"name": "Library Assert",
"publisher": "Microsoft",
"version": "20.0.0.0"
}
]
}
```
## Rule 4: Unit Testing Best Practices
### Intent
Write comprehensive unit tests that ensure reliability of business logic.
- Write unit tests for all business logic
- Follow given/when/then structure for test naming
- Use Assert statements for validating critical conditions
- Create test data factories for consistent test setup
- Always try to use standard library codeunits to create data and post documents
- When creating tests, use descriptive names that follow given/when/then pattern and include comprehensive assertions to validate expected behavior.
### Examples
```al
// Good example - Well-structured unit test with standard library codeunits
codeunit 50200 "Customer Management Tests"
{
Subtype = Test;
var
Assert: Codeunit Assert;
LibrarySales: Codeunit "Library - Sales";
LibraryInventory: Codeunit "Library - Inventory";
LibraryRandom: Codeunit "Library - Random";
LibraryERM: Codeunit "Library - ERM";
[Test]
procedure GivenValidCustomer_WhenCreatingCustomer_ThenCustomerIsCreated()
var
Customer: Record Customer;
CustomerManagement: Codeunit "Customer Management";
CustomerNo: Code[20];
begin
// Given - Valid customer data using library
LibrarySales.CreateCustomer(Customer);
Customer."Credit Limit (LCY)" := LibraryRandom.RandDec(10000, 2);
// When - Creating customer
CustomerNo := CustomerManagement.CreateCustomer(Customer);
// Then - Customer is created successfully
Assert.IsTrue(Customer.Get(CustomerNo), 'Customer should be created');
Assert.AreEqual(Customer.Name, Customer.Name, 'Customer name should match');
end;
[Test]
procedure GivenSalesOrder_WhenPostingOrder_ThenInvoiceIsCreated()
var
SalesHeader: Record "Sales Header";
SalesLine: Record "Sales Line";
Item: Record Item;
PostedInvoiceNo: Code[20];
begin
// Given - Sales order with library-created data
LibraryInventory.CreateItem(Item);
LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, '');
LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", LibraryRandom.RandInt(10));
// When - Posting sales order
PostedInvoiceNo := LibrarySales.PostSalesDocument(SalesHeader, true, true);
// Then - Posted invoice exists
Assert.AreNotEqual('', PostedInvoiceNo, 'Posted invoice should be created');
end;
}
```
## Rule 5: Feature-Based Test Organization
### Intent
Organize test files to mirror the application structure while maintaining clear separation.
- Test files should mirror App project structure in Test project
- Use same feature-based organization for tests
- Place shared test utilities in Common folder
- Maintain consistent naming patterns
### Examples
```
// Good example - Mirrored test structure
App/src/
├── NoSeries/
│ ├── NoSeries.Table.al
│ └── NoSeries.Page.al
└── Sales/
└── Invoice/
└── SalesInvoice.Page.al
Test/src/
├── NoSeries/
│ └── NoSeriesTests.Codeunit.al
├── Sales/
│ └── Invoice/
│ └── SalesInvoiceTests.Codeunit.al
└── Common/
└── TestHelpers/
└── TestDataFactory.Codeunit.al
```