Add comprehensive agentic coding documentation

- Add 6 MCP server documentation pages (bc-code-intelligence, al-dependency, azure-devops, serena, al-objid, clockify)
- Add AI Coding Agents documentation (GitHub Copilot, Claude, Cursor)
- Add Getting Started section (what is agentic coding, setup, prompting, best practices, limitations, glossary)
- Add Getting More section (code review, documentation, telemetry examples)
- Add Community Resources placeholders (articles, videos, tools)
- Update Tools section to focus on MCP servers instead of VSCode extensions
This commit is contained in:
Jeremy Vyska 2025-10-10 12:25:41 +02:00
parent 3254d9b7e8
commit 4db38ba4d3
28 changed files with 8152 additions and 0 deletions

View file

@ -0,0 +1,47 @@
---
title: "Getting More"
linkTitle: "Getting More"
weight: 20
description: >
Practical examples and advanced techniques for AI-assisted AL development
---
This section provides hands-on examples of how to use AI assistants for common AL development tasks. Each guide walks through a realistic scenario with step-by-step instructions and prompts.
## In This Section
- **[AI-Assisted Code Review](code-review)** - Use AI to review code for quality, performance, and best practices
- **[Generating Documentation](documentation)** - Automatically create and maintain documentation for your AL code
- **[Adding Telemetry](telemetry)** - Instrument your extensions with Application Insights telemetry
- **[Refactoring Legacy Code](refactoring)** - Modernize and improve existing AL code
- **[Writing Tests](testing)** - Create comprehensive test coverage with AI assistance
## How to Use These Guides
Each guide follows a practical, scenario-based approach:
1. **Scenario**: A realistic development task
2. **Goal**: What you're trying to achieve
3. **Step-by-Step**: Detailed instructions with actual prompts
4. **Review Points**: What to check in the AI-generated code
5. **Tips**: Additional insights and variations
## Before You Start
Make sure you've completed the [Getting Started](../getting-started) section to:
- Understand agentic coding concepts
- Set up your environment
- Learn effective prompting
- Know the best practices and limitations
## Learning by Doing
These examples are designed to be:
- **Practical**: Based on real AL development tasks
- **Detailed**: Step-by-step instructions you can follow
- **Educational**: Explains why, not just what
- **Adaptable**: Patterns you can apply to your own work
## Contributing Examples
Have a great example of AI-assisted AL development? Consider contributing! See the [Contributing](../../contributing) section for guidelines.

View file

@ -0,0 +1,368 @@
---
title: "AI-Assisted Code Review"
linkTitle: "Code Review"
weight: 1
description: >
Learn how to use AI assistants to review AL code for quality, performance, and best practices
---
## Scenario
You've just finished implementing a new feature: a codeunit that processes sales orders and applies volume-based discounts. Before submitting for peer review, you want to use AI to catch potential issues.
## Goal
Use AI to identify:
- Potential bugs or logical errors
- Performance issues
- AL best practice violations
- Missing error handling
- Code quality improvements
## The Code to Review
Here's the codeunit we'll review:
```al
codeunit 50100 "Sales Order Discount Processor"
{
procedure ApplyVolumeDiscounts(var SalesHeader: Record "Sales Header")
var
SalesLine: Record "Sales Line";
TotalQuantity: Decimal;
DiscountPct: Decimal;
begin
TotalQuantity := 0;
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
SalesLine.SetRange("Document No.", SalesHeader."No.");
if SalesLine.FindSet() then
repeat
TotalQuantity := TotalQuantity + SalesLine.Quantity;
until SalesLine.Next() = 0;
if TotalQuantity > 100 then
DiscountPct := 15
else if TotalQuantity > 50 then
DiscountPct := 10
else if TotalQuantity > 10 then
DiscountPct := 5;
SalesLine.Reset();
if SalesLine.FindSet() then
repeat
SalesLine."Line Discount %" := DiscountPct;
SalesLine.Modify();
until SalesLine.Next() = 0;
end;
}
```
## Step-by-Step Review Process
### Step 1: Initial Quality Review
**Prompt**:
```
Review this AL codeunit for potential bugs, code quality issues, and best practice violations.
[Paste the code above]
Specifically check for:
- Logical errors
- Missing error handling
- Variable declarations
- Commit/transaction issues
- Performance concerns
```
**Expected AI Findings**:
The AI should identify issues like:
- Missing `FindSet(true, false)` parameters for modification
- No error handling
- Multiple FindSet operations (inefficient)
- Hard-coded discount percentages
- No validation of input parameters
- Missing ModifyAll opportunity
- No telemetry or logging
### Step 2: Performance Analysis
**Prompt**:
```
Analyze the performance of this code, especially database operations.
What are potential performance issues? How can it be optimized?
[Include the code or reference it if in context]
```
**Expected AI Suggestions**:
- Combine the two FindSet loops into one
- Use ModifyAll instead of looping for discount application
- Consider using SetLoadFields for better performance
- Cache discount percentages instead of hard-coding
### Step 3: Best Practices Check
**Prompt**:
```
Check this code against AL coding best practices and Business Central development standards.
What improvements would you recommend?
```
**Expected AI Recommendations**:
- Add XML documentation
- Extract discount calculation to separate function
- Use constants for discount tiers
- Add error handling
- Implement proper transaction handling
- Add telemetry for monitoring
### Step 4: Request Improved Version
**Prompt**:
```
Rewrite this codeunit implementing all the improvements you suggested.
Include:
- XML documentation
- Error handling
- Performance optimizations
- Best practice compliance
- Telemetry/logging
- Discount tiers from setup table instead of hard-coded values
```
**Review the Generated Code**:
The AI should generate something like:
```al
/// <summary>
/// Processes sales orders and applies volume-based discounts
/// </summary>
codeunit 50100 "Sales Order Discount Processor"
{
/// <summary>
/// Applies volume-based discounts to a sales order
/// </summary>
/// <param name="SalesHeader">The sales header to process</param>
procedure ApplyVolumeDiscounts(var SalesHeader: Record "Sales Header")
var
SalesLine: Record "Sales Line";
TotalQuantity: Decimal;
DiscountPct: Decimal;
begin
if SalesHeader."No." = '' then
Error('Sales order number cannot be empty.');
TotalQuantity := CalculateTotalQuantity(SalesHeader);
DiscountPct := GetDiscountPercentage(TotalQuantity);
if DiscountPct > 0 then
ApplyDiscountToLines(SalesHeader, DiscountPct);
LogTelemetry(SalesHeader."No.", TotalQuantity, DiscountPct);
end;
local procedure CalculateTotalQuantity(SalesHeader: Record "Sales Header"): Decimal
var
SalesLine: Record "Sales Line";
TotalQty: Decimal;
begin
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
SalesLine.SetRange("Document No.", SalesHeader."No.");
SalesLine.SetLoadFields(Quantity);
if SalesLine.FindSet() then
repeat
TotalQty += SalesLine.Quantity;
until SalesLine.Next() = 0;
exit(TotalQty);
end;
local procedure GetDiscountPercentage(TotalQuantity: Decimal): Decimal
var
VolumeDiscountSetup: Record "Volume Discount Setup"; // Assuming this table exists
begin
VolumeDiscountSetup.SetFilter("Minimum Quantity", '<=%1', TotalQuantity);
VolumeDiscountSetup.SetCurrentKey("Minimum Quantity");
VolumeDiscountSetup.Ascending(false);
if VolumeDiscountSetup.FindFirst() then
exit(VolumeDiscountSetup."Discount Percentage");
exit(0);
end;
local procedure ApplyDiscountToLines(SalesHeader: Record "Sales Header"; DiscountPct: Decimal)
var
SalesLine: Record "Sales Line";
begin
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
SalesLine.SetRange("Document No.", SalesHeader."No.");
SalesLine.ModifyAll("Line Discount %", DiscountPct, true);
end;
local procedure LogTelemetry(DocumentNo: Code[20]; TotalQty: Decimal; DiscountPct: Decimal)
var
Telemetry: Codeunit Telemetry;
CustomDimensions: Dictionary of [Text, Text];
begin
CustomDimensions.Add('DocumentNo', DocumentNo);
CustomDimensions.Add('TotalQuantity', Format(TotalQty));
CustomDimensions.Add('DiscountPercentage', Format(DiscountPct));
Telemetry.LogMessage('0001', 'Volume discount applied', Verbosity::Normal,
DataClassification::SystemMetadata, TelemetryScope::All, CustomDimensions);
end;
}
```
## Review Points: What to Check
After AI generates the improved code, verify:
### ✅ Correctness
- [ ] Logic matches business requirements
- [ ] All edge cases handled
- [ ] No regression in functionality
- [ ] Error messages are clear
### ✅ Performance
- [ ] Efficient database queries
- [ ] Proper use of SetLoadFields
- [ ] ModifyAll used where appropriate
- [ ] No unnecessary loops
### ✅ Best Practices
- [ ] XML documentation complete
- [ ] Proper error handling
- [ ] Appropriate transaction handling
- [ ] Good function decomposition
### ✅ AL Specifics
- [ ] Correct AL syntax
- [ ] Proper use of BC APIs
- [ ] No deprecated features
- [ ] Appropriate data types
### ✅ Maintainability
- [ ] Clear, descriptive names
- [ ] Logical organization
- [ ] Easy to test
- [ ] Well documented
## Additional Review Prompts
### Security Review
```
Review this code for potential security issues:
- Input validation
- Authorization checks
- Data access controls
- Potential injection vulnerabilities
```
### Testability Review
```
Analyze this code for testability.
What makes it easy or hard to test?
How can it be restructured to be more testable?
```
### Documentation Review
```
Review the XML documentation for this code.
Is it complete? Clear? Helpful?
What additional documentation would be valuable?
```
## Common Issues AI Might Miss
Be aware AI might not catch:
1. **Business Logic Errors**
- AI doesn't know your specific discount rules
- Verify the logic matches actual requirements
2. **Integration Issues**
- AI doesn't know about other extensions
- Check for conflicts with existing code
3. **BC Version Compatibility**
- AI might use features not in your BC version
- Verify all APIs are available
4. **Organization Standards**
- AI doesn't know your specific standards
- Adapt to match your conventions
## Best Practices for AI Code Review
### 1. Use Specific Review Criteria
Instead of "review this code", specify what to look for:
```
Review for: performance, error handling, AL best practices, testability
```
### 2. Review in Layers
Don't try to review everything at once:
- First: Correctness and logic
- Second: Performance
- Third: Best practices
- Fourth: Documentation
### 3. Combine with Tools
Use AI review alongside:
- AL code analyzers
- Static analysis tools
- Peer review
- Testing
### 4. Iterate
Review, improve, review again:
```
Review the updated code. Are there any remaining issues?
```
### 5. Document Findings
Keep track of:
- Common issues AI finds
- Issues AI misses
- Effective review prompts
## Practice Exercise
Try reviewing this code with AI:
```al
procedure CalculateShippingCost(SalesHeader: Record "Sales Header"): Decimal
var
SalesLine: Record "Sales Line";
Weight: Decimal;
begin
Weight := 0;
SalesLine.SetRange("Document No.", SalesHeader."No.");
if SalesLine.Find('-') then
repeat
Weight := Weight + SalesLine.Quantity;
until SalesLine.Next() = 0;
if Weight < 10 then
exit(5.00)
else
exit(10.00);
end;
```
**Your Tasks**:
1. Ask AI to review for issues
2. Request performance improvements
3. Ask for best practice compliance
4. Get an improved version
5. Review the improved version yourself
## Next Steps
- Learn how to use AI for [generating documentation](../documentation)
- See how AI can help with [adding telemetry](../telemetry)
- Explore [refactoring legacy code](../refactoring) with AI assistance

View file

@ -0,0 +1,661 @@
---
title: "Generating Documentation"
linkTitle: "Documentation"
weight: 2
description: >
Use AI to create and maintain comprehensive documentation for your AL code
---
## Scenario
You have a well-functioning AL extension but it lacks documentation. You need to create:
- XML documentation comments for all procedures
- A comprehensive README file
- API documentation for public interfaces
- User-facing feature documentation
## Goal
Use AI to quickly generate high-quality documentation that:
- Explains what the code does
- Documents parameters and return values
- Provides usage examples
- Helps developers and users understand the extension
## Types of Documentation
### 1. XML Documentation Comments
In-code documentation for procedures, triggers, and events.
### 2. README Files
Project overview, setup instructions, and getting started guides.
### 3. API Documentation
Documentation for public APIs and integration points.
### 4. User Documentation
End-user guides and feature explanations.
## Step-by-Step Guide
### Step 1: XML Documentation for Procedures
**Scenario**: You have a codeunit without documentation.
**Starting Code**:
```al
codeunit 50100 "Customer Email Manager"
{
procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean
var
Customer: Record Customer;
EmailMessage: Codeunit "Email Message";
begin
if not Customer.Get(CustomerNo) then
exit(false);
if Customer."E-Mail" = '' then
exit(false);
EmailMessage.Create(Customer."E-Mail", 'Welcome!', GetWelcomeEmailBody(Customer));
exit(EmailMessage.Send());
end;
local procedure GetWelcomeEmailBody(Customer: Record Customer): Text
begin
exit(StrSubstNo('Dear %1, welcome to our service!', Customer.Name));
end;
}
```
**Prompt**:
```
Add XML documentation comments to all procedures in this codeunit.
Include:
- Summary for each procedure
- Parameter descriptions
- Return value descriptions
- Example usage where helpful
- Any important notes or warnings
[Paste the code]
```
**Expected Result**:
```al
/// <summary>
/// Manages customer email communications
/// </summary>
codeunit 50100 "Customer Email Manager"
{
/// <summary>
/// Sends a welcome email to a customer
/// </summary>
/// <param name="CustomerNo">The customer number to send the email to</param>
/// <returns>True if the email was sent successfully, false otherwise</returns>
/// <remarks>
/// The procedure will return false if:
/// - The customer does not exist
/// - The customer has no email address
/// - The email fails to send
/// </remarks>
/// <example>
/// if CustomerEmailManager.SendWelcomeEmail('C001') then
/// Message('Welcome email sent successfully');
/// </example>
procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean
var
Customer: Record Customer;
EmailMessage: Codeunit "Email Message";
begin
if not Customer.Get(CustomerNo) then
exit(false);
if Customer."E-Mail" = '' then
exit(false);
EmailMessage.Create(Customer."E-Mail", 'Welcome!', GetWelcomeEmailBody(Customer));
exit(EmailMessage.Send());
end;
/// <summary>
/// Generates the body text for the welcome email
/// </summary>
/// <param name="Customer">The customer record to generate the email for</param>
/// <returns>The formatted email body text</returns>
local procedure GetWelcomeEmailBody(Customer: Record Customer): Text
begin
exit(StrSubstNo('Dear %1, welcome to our service!', Customer.Name));
end;
}
```
### Step 2: Batch Documentation for Multiple Files
**Prompt**:
```
I have multiple AL files that need XML documentation.
For each public procedure, add:
- Summary
- Parameter descriptions
- Return value (if applicable)
- Example usage
Process these files:
[List files or paste multiple code blocks]
```
**Tip**: Do this in batches of 3-5 files to maintain context and quality.
### Step 3: Creating a README File
**Prompt**:
```
Create a comprehensive README.md for my AL extension with the following information:
Extension Name: Customer Engagement Suite
Purpose: Enhance customer communication and engagement features
Key Features:
- Automated welcome emails
- Customer portal access
- Newsletter subscription management
- Customer satisfaction surveys
Include sections for:
- Overview and purpose
- Features list
- Installation instructions
- Configuration steps
- Usage examples
- Dependencies
- Compatibility (BC version 21+)
- Contributing guidelines
- License (MIT)
```
**Expected Structure**:
```markdown
# Customer Engagement Suite
> Enhance customer communication and engagement in Microsoft Dynamics 365 Business Central
## Overview
The Customer Engagement Suite is a Business Central extension that provides comprehensive
tools for managing customer communications and engagement activities.
## Features
### Automated Welcome Emails
Automatically send personalized welcome emails to new customers upon registration.
### Customer Portal Access
Enable customers to access a self-service portal for order tracking and account management.
### Newsletter Subscription Management
Allow customers to manage their newsletter preferences and subscriptions.
### Customer Satisfaction Surveys
Collect and analyze customer feedback through integrated surveys.
## Installation
### Prerequisites
- Microsoft Dynamics 365 Business Central version 21.0 or higher
- Email account configured in Business Central
- System Application extension installed
### Steps
1. Download the extension file (.app)
2. Open Business Central Administration Shell
3. Run the installation command:
```powershell
Publish-NAVApp -ServerInstance BC210 -Path "CustomerEngagementSuite.app"
Install-NAVApp -ServerInstance BC210 -Name "Customer Engagement Suite"
```
## Configuration
### Email Setup
1. Navigate to **Email Accounts** page
2. Configure your SMTP settings
3. Test the email connection
### Welcome Email Template
1. Go to **Customer Engagement Setup**
2. Configure the welcome email template
3. Enable automatic sending
## Usage
### Sending Welcome Emails
```al
codeunit 50100 "Customer Email Manager"
{
procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean
}
```
**Example:**
```al
CustomerEmailManager: Codeunit "Customer Email Manager";
begin
if CustomerEmailManager.SendWelcomeEmail('C001') then
Message('Welcome email sent successfully');
end;
```
## Dependencies
- **System Application**: Core BC functionality
- **Base Application**: Standard BC tables and pages
## Compatibility
- **Business Central**: Version 21.0 and higher
- **Platform**: Compatible with both SaaS and On-Premises
## Contributing
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## License
This project is licensed under the MIT License - see [LICENSE](LICENSE) file for details.
## Support
For issues and questions:
- Create an issue in our [GitHub repository](https://github.com/yourorg/customer-engagement)
- Email: support@yourcompany.com
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for version history.
```
### Step 4: API Documentation
**Prompt**:
```
Create API documentation for the public procedures in my Customer Email Manager codeunit.
Format it as markdown suitable for a developer portal.
Include:
- Procedure signature
- Description
- Parameters with types and descriptions
- Return values
- Code examples
- Error conditions
- Notes and best practices
[Include the codeunit code]
```
**Expected Output**:
```markdown
# Customer Email Manager API
## Overview
The Customer Email Manager provides functionality for sending customer-related emails.
## Procedures
### SendWelcomeEmail
Sends a welcome email to a specified customer.
**Signature:**
```al
procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean
```
**Parameters:**
| Name | Type | Description |
|------|------|-------------|
| CustomerNo | Code[20] | The unique identifier of the customer |
**Returns:**
| Type | Description |
|------|-------------|
| Boolean | `true` if email sent successfully, `false` otherwise |
**Example:**
```al
var
CustomerEmailManager: Codeunit "Customer Email Manager";
Success: Boolean;
begin
Success := CustomerEmailManager.SendWelcomeEmail('C00001');
if Success then
Message('Email sent successfully')
else
Error('Failed to send email');
end;
```
**Error Conditions:**
The procedure returns `false` in the following cases:
- Customer does not exist
- Customer has no email address configured
- Email service is unavailable
**Best Practices:**
- Always check the return value
- Ensure email account is configured before calling
- Consider calling asynchronously for better performance
**See Also:**
- Email Account Setup
- Customer Configuration
```
### Step 5: User Documentation
**Prompt**:
```
Create user-facing documentation for the Welcome Email feature.
Target audience: Business users who are not developers.
Explain:
- What the feature does
- How to enable/configure it
- How to use it
- Troubleshooting common issues
Use simple language and include screenshots placeholders where helpful.
```
**Expected Output**:
```markdown
# Welcome Email Feature
## What is the Welcome Email Feature?
The Welcome Email feature automatically sends a personalized email to new customers
when they are added to your system. This helps create a positive first impression
and provides important information to your customers right away.
## Enabling Welcome Emails
1. Open Business Central
2. Search for "Customer Engagement Setup"
3. Check the box next to "Send Welcome Emails"
4. Click "OK" to save
![Setup Screenshot]
## Configuring the Email Template
### Step 1: Access Email Templates
1. Search for "Email Templates"
2. Find "Customer Welcome Email"
3. Click "Edit"
### Step 2: Customize the Message
1. Update the email subject line
2. Modify the email body
3. Add your company logo
4. Click "OK" to save
### Step 3: Test the Email
1. Open a test customer
2. Click "Actions" > "Send Welcome Email"
3. Check that you receive the email
## Using Welcome Emails
### Automatic Sending
When enabled, welcome emails are sent automatically when:
- A new customer is created
- An existing customer is marked as active for the first time
### Manual Sending
You can also send welcome emails manually:
1. Open the customer card
2. Click "Actions" > "Email" > "Send Welcome Email"
3. Confirm the action
## Troubleshooting
### Email Not Sending
**Problem:** Welcome emails are not being sent
**Solutions:**
- Check that the feature is enabled in setup
- Verify the customer has an email address
- Confirm your email account is configured correctly
- Check that email service is running
### Wrong Email Template
**Problem:** Customers receive the wrong email content
**Solutions:**
- Review the email template configuration
- Ensure you're editing the correct template
- Clear any cached templates
### Customer Not Receiving Email
**Problem:** Email shows as sent but customer didn't receive it
**Solutions:**
- Ask customer to check spam folder
- Verify the email address is correct
- Check email delivery logs
- Test with a different email address
## Tips and Best Practices
- **Personalization**: Use customer name and company name in the email
- **Timing**: Send welcome emails within 24 hours of customer creation
- **Follow-up**: Consider a follow-up email series
- **Feedback**: Ask customers if they received and found the email helpful
## Need Help?
Contact your Business Central administrator or support team for assistance.
```
## Review Points
After generating documentation, verify:
### ✅ Accuracy
- [ ] Documentation matches actual code behavior
- [ ] Parameter descriptions are correct
- [ ] Return values accurately described
- [ ] Examples work as shown
### ✅ Completeness
- [ ] All public procedures documented
- [ ] All parameters explained
- [ ] Edge cases mentioned
- [ ] Error conditions documented
### ✅ Clarity
- [ ] Easy to understand
- [ ] Appropriate level of detail
- [ ] Good examples provided
- [ ] Proper formatting
### ✅ Maintenance
- [ ] Easy to update
- [ ] Versioned appropriately
- [ ] Consistent style
- [ ] Well organized
## Advanced Documentation Prompts
### Generate CHANGELOG
```
Create a CHANGELOG.md file documenting these changes from version 1.0 to 2.0:
- Added: Customer portal access
- Added: Newsletter preferences
- Changed: Welcome email now includes portal link
- Fixed: Email encoding issues with special characters
- Deprecated: Old email API (will be removed in 3.0)
Follow Keep a Changelog format.
```
### Create Migration Guide
```
Create a migration guide for users upgrading from version 1.x to 2.0.
Include:
- Breaking changes
- New features
- Configuration changes needed
- Data migration steps
- Deprecation warnings
```
### Generate Inline Code Comments
```
Add helpful inline comments to this complex procedure explaining the logic flow.
Don't over-comment obvious code, but do explain:
- Complex algorithms
- Business rule implementations
- Non-obvious optimizations
- Workarounds
[Paste code]
```
## Keeping Documentation Updated
### When Code Changes
**Prompt**:
```
I've updated this procedure to add a new parameter.
Update the XML documentation to reflect the change:
Old procedure:
[paste old code]
New procedure:
[paste new code]
```
### Regular Documentation Reviews
**Prompt**:
```
Review the documentation for this codeunit.
Check for:
- Outdated information
- Missing documentation
- Incorrect examples
- Deprecated features
[Paste codeunit]
```
## Best Practices
### 1. Document As You Code
```
I'm about to write a procedure to validate customer credit limits.
Create the XML documentation comment first, then we'll implement the procedure.
```
### 2. Use Consistent Style
Create a documentation template:
```
Create an XML documentation template I can use for all my procedures.
Include sections for: summary, parameters, returns, exceptions, examples, and remarks.
```
### 3. Generate Documentation in Batches
Document related code together for consistency:
```
Document all procedures in this codeunit that relate to email sending.
Use consistent terminology and structure.
```
### 4. Include Real Examples
```
Add a realistic code example to this procedure's documentation showing:
- Typical usage
- Error handling
- Integration with other features
```
## Common Documentation Patterns
### For Validation Procedures
```
Document this validation procedure. Include:
- What is being validated
- Valid conditions
- Error messages that can be raised
- Example of valid and invalid inputs
```
### For Event Subscribers
```
Document this event subscriber. Include:
- What event it subscribes to
- When it triggers
- What it does
- Side effects or implications
- Integration points
```
### For APIs
```
Create REST API documentation for this AL API page.
Include:
- Endpoint URL
- HTTP methods supported
- Request/response examples
- Authentication requirements
- Error codes
```
## Practice Exercise
Generate documentation for this code:
```al
codeunit 50110 "Order Status Manager"
{
procedure UpdateOrderStatus(OrderNo: Code[20]; NewStatus: Enum "Order Status"): Boolean
var
SalesHeader: Record "Sales Header";
begin
if not SalesHeader.Get(SalesHeader."Document Type"::Order, OrderNo) then
exit(false);
SalesHeader.Status := NewStatus;
SalesHeader.Modify(true);
SendStatusNotification(OrderNo, NewStatus);
exit(true);
end;
local procedure SendStatusNotification(OrderNo: Code[20]; Status: Enum "Order Status")
begin
// Implementation
end;
}
```
**Your Tasks**:
1. Generate XML documentation
2. Create a README section explaining this feature
3. Write API documentation
4. Create user documentation
5. Review and improve the generated docs
## Next Steps
- Learn how to use AI for [adding telemetry](../telemetry)
- Explore [refactoring legacy code](../refactoring) while maintaining documentation
- See how to conduct [AI-assisted code reviews](../code-review)

View file

@ -0,0 +1,599 @@
---
title: "Adding Telemetry"
linkTitle: "Telemetry"
weight: 3
description: >
Use AI to instrument your AL extensions with Application Insights telemetry
---
## Scenario
You have a Business Central extension that's running in production, but you have limited visibility into:
- How often features are used
- Where errors occur
- Performance bottlenecks
- User behavior patterns
You want to add comprehensive telemetry using Application Insights to monitor your extension in production.
## Goal
Use AI to help you:
- Add telemetry calls throughout your code
- Log important events and errors
- Track feature usage
- Monitor performance
- Create custom dimensions for better analysis
## Prerequisites
Understanding of:
- Business Central telemetry concepts
- Application Insights basics
- When to log (without over-logging)
- Data classification requirements
## Step-by-Step Guide
### Step 1: Understanding Your Code
**Starting Code** (without telemetry):
```al
codeunit 50100 "Sales Order Processor"
{
procedure ProcessOrder(var SalesHeader: Record "Sales Header"): Boolean
var
SalesLine: Record "Sales Line";
begin
if not ValidateOrder(SalesHeader) then
exit(false);
CalculateTotals(SalesHeader);
ApplyDiscounts(SalesHeader);
if not PostOrder(SalesHeader) then
exit(false);
exit(true);
end;
local procedure ValidateOrder(var SalesHeader: Record "Sales Header"): Boolean
var
Customer: Record Customer;
begin
if not Customer.Get(SalesHeader."Sell-to Customer No.") then begin
Error('Customer %1 does not exist.', SalesHeader."Sell-to Customer No.");
end;
if Customer.Blocked <> Customer.Blocked::" " then begin
Error('Customer %1 is blocked.', Customer."No.");
end;
exit(true);
end;
local procedure PostOrder(var SalesHeader: Record "Sales Header"): Boolean
var
SalesPost: Codeunit "Sales-Post";
begin
exit(SalesPost.Run(SalesHeader));
end;
}
```
### Step 2: Request Telemetry Strategy
**Prompt**:
```
I want to add Application Insights telemetry to this Business Central codeunit.
Help me plan what to log:
- What events should be logged?
- What custom dimensions should be included?
- What data classification is appropriate?
- Where should we log errors vs. information?
[Paste the code above]
```
**Expected AI Response**:
The AI should suggest logging:
**Events to Log:**
1. Order processing started (Informational)
2. Order validation completed (Informational)
3. Order posted successfully (Informational)
4. Validation failures (Warning)
5. Posting failures (Error)
6. Performance metrics (Informational)
**Custom Dimensions:**
- Order Number
- Customer Number
- Order Amount
- Number of Lines
- Processing Duration
- Error Details (when applicable)
**Data Classification:**
- Order Number: CustomerContent
- Customer Number: CustomerContent
- Amounts: CustomerContent
- Counts: SystemMetadata
- Duration: SystemMetadata
### Step 3: Add Telemetry to Main Procedure
**Prompt**:
```
Add Application Insights telemetry to the ProcessOrder procedure.
Include:
- Start event when processing begins
- Success event when complete
- Error event on failure
- Custom dimensions: OrderNo, CustomerNo, TotalAmount, LineCount, Duration
- Use appropriate data classification
- Use the Telemetry codeunit from System Application
Update this code:
[Paste ProcessOrder procedure]
```
**Expected Result**:
```al
procedure ProcessOrder(var SalesHeader: Record "Sales Header"): Boolean
var
Telemetry: Codeunit Telemetry;
CustomDimensions: Dictionary of [Text, Text];
StartTime: DateTime;
Duration: Duration;
LineCount: Integer;
begin
StartTime := CurrentDateTime;
// Log start of processing
AddOrderDimensions(CustomDimensions, SalesHeader);
Telemetry.LogMessage('SALESORD-001', 'Order processing started',
Verbosity::Normal, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
if not ValidateOrder(SalesHeader) then begin
LogProcessingError(SalesHeader, 'Validation failed');
exit(false);
end;
CalculateTotals(SalesHeader);
ApplyDiscounts(SalesHeader);
if not PostOrder(SalesHeader) then begin
LogProcessingError(SalesHeader, 'Posting failed');
exit(false);
end;
// Log successful completion
Duration := CurrentDateTime - StartTime;
Clear(CustomDimensions);
AddOrderDimensions(CustomDimensions, SalesHeader);
CustomDimensions.Add('Duration', Format(Duration));
CustomDimensions.Add('LineCount', Format(CountLines(SalesHeader)));
Telemetry.LogMessage('SALESORD-002', 'Order processing completed successfully',
Verbosity::Normal, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
exit(true);
end;
local procedure AddOrderDimensions(var CustomDimensions: Dictionary of [Text, Text]; SalesHeader: Record "Sales Header")
begin
CustomDimensions.Add('OrderNo', SalesHeader."No.");
CustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No.");
CustomDimensions.Add('OrderType', Format(SalesHeader."Document Type"));
end;
local procedure LogProcessingError(SalesHeader: Record "Sales Header"; ErrorMessage: Text)
var
Telemetry: Codeunit Telemetry;
CustomDimensions: Dictionary of [Text, Text];
begin
AddOrderDimensions(CustomDimensions, SalesHeader);
CustomDimensions.Add('ErrorMessage', ErrorMessage);
CustomDimensions.Add('ErrorCallStack', GetLastErrorCallStack());
Telemetry.LogMessage('SALESORD-E001', StrSubstNo('Order processing failed: %1', ErrorMessage),
Verbosity::Error, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
end;
local procedure CountLines(SalesHeader: Record "Sales Header"): Integer
var
SalesLine: Record "Sales Line";
begin
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
SalesLine.SetRange("Document No.", SalesHeader."No.");
exit(SalesLine.Count);
end;
```
### Step 4: Add Error Tracking
**Prompt**:
```
Update the ValidateOrder procedure to log validation failures with telemetry.
For each validation check that fails:
- Log a warning message
- Include relevant details (customer number, block reason, etc.)
- Use appropriate custom dimensions
- Use proper data classification
[Paste ValidateOrder procedure]
```
**Expected Result**:
```al
local procedure ValidateOrder(var SalesHeader: Record "Sales Header"): Boolean
var
Customer: Record Customer;
Telemetry: Codeunit Telemetry;
CustomDimensions: Dictionary of [Text, Text];
begin
if not Customer.Get(SalesHeader."Sell-to Customer No.") then begin
AddValidationDimensions(CustomDimensions, SalesHeader, 'Customer not found');
Telemetry.LogMessage('SALESORD-W001', 'Order validation failed: Customer not found',
Verbosity::Warning, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
Error('Customer %1 does not exist.', SalesHeader."Sell-to Customer No.");
end;
if Customer.Blocked <> Customer.Blocked::" " then begin
Clear(CustomDimensions);
AddValidationDimensions(CustomDimensions, SalesHeader, 'Customer blocked');
CustomDimensions.Add('BlockedReason', Format(Customer.Blocked));
Telemetry.LogMessage('SALESORD-W002', 'Order validation failed: Customer blocked',
Verbosity::Warning, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
Error('Customer %1 is blocked.', Customer."No.");
end;
exit(true);
end;
local procedure AddValidationDimensions(var CustomDimensions: Dictionary of [Text, Text]; SalesHeader: Record "Sales Header"; Reason: Text)
begin
CustomDimensions.Add('OrderNo', SalesHeader."No.");
CustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No.");
CustomDimensions.Add('ValidationFailure', Reason);
end;
```
### Step 5: Add Performance Tracking
**Prompt**:
```
Add performance telemetry to track how long different operations take.
Track duration for:
- Overall processing
- Validation
- Calculation
- Posting
Log if any operation takes longer than expected thresholds:
- Validation > 1 second
- Calculation > 2 seconds
- Posting > 5 seconds
[Include the codeunit]
```
**Expected Result**:
```al
local procedure ValidateOrder(var SalesHeader: Record "Sales Header"): Boolean
var
Customer: Record Customer;
Telemetry: Codeunit Telemetry;
CustomDimensions: Dictionary of [Text, Text];
StartTime: DateTime;
Duration: Duration;
begin
StartTime := CurrentDateTime;
// Validation logic here...
Duration := CurrentDateTime - StartTime;
// Log if validation took too long
if Duration > 1000 then begin
AddOrderDimensions(CustomDimensions, SalesHeader);
CustomDimensions.Add('Duration', Format(Duration));
CustomDimensions.Add('Operation', 'Validation');
Telemetry.LogMessage('SALESORD-P001', 'Slow validation detected',
Verbosity::Warning, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
end;
exit(true);
end;
```
### Step 6: Add Feature Usage Tracking
**Prompt**:
```
Add telemetry to track feature usage for business analytics.
Track:
- How often each discount type is applied
- Which payment methods are used
- Popular shipping methods
- Order size distribution
Create a FeatureUsageTelemetry codeunit for this purpose.
```
**Expected Result**:
```al
codeunit 50101 "Feature Usage Telemetry"
{
var
Telemetry: Codeunit Telemetry;
procedure LogDiscountApplied(DiscountType: Text; DiscountPct: Decimal; OrderNo: Code[20])
var
CustomDimensions: Dictionary of [Text, Text];
begin
CustomDimensions.Add('FeatureArea', 'Discounts');
CustomDimensions.Add('DiscountType', DiscountType);
CustomDimensions.Add('DiscountPercentage', Format(DiscountPct));
CustomDimensions.Add('OrderNo', OrderNo);
Telemetry.LogMessage('FEATURE-DISCOUNT', 'Discount applied',
Verbosity::Normal, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
end;
procedure LogPaymentMethod(PaymentMethodCode: Code[10]; Amount: Decimal)
var
CustomDimensions: Dictionary of [Text, Text];
begin
CustomDimensions.Add('FeatureArea', 'Payment');
CustomDimensions.Add('PaymentMethod', PaymentMethodCode);
CustomDimensions.Add('AmountRange', GetAmountRange(Amount));
Telemetry.LogMessage('FEATURE-PAYMENT', 'Payment method used',
Verbosity::Normal, DataClassification::SystemMetadata,
TelemetryScope::ExtensionPublisher, CustomDimensions);
end;
local procedure GetAmountRange(Amount: Decimal): Text
begin
case true of
Amount < 100:
exit('0-100');
Amount < 1000:
exit('100-1000');
Amount < 10000:
exit('1000-10000');
else
exit('10000+');
end;
end;
}
```
## Telemetry Best Practices
### ✅ Do's
**Use Consistent Event IDs**
```
SALESORD-001: Processing started
SALESORD-002: Processing completed
SALESORD-E001: Processing error
SALESORD-W001: Validation warning
SALESORD-P001: Performance warning
```
**Use Meaningful Messages**
```al
// Good
Telemetry.LogMessage('SALESORD-001', 'Sales order processing started for large order', ...);
// Bad
Telemetry.LogMessage('001', 'Started', ...);
```
**Include Helpful Custom Dimensions**
```al
CustomDimensions.Add('OrderNo', OrderNo);
CustomDimensions.Add('CustomerNo', CustomerNo);
CustomDimensions.Add('LineCount', Format(LineCount));
CustomDimensions.Add('TotalAmount', Format(TotalAmount));
CustomDimensions.Add('ProcessingDuration', Format(Duration));
```
**Use Appropriate Data Classification**
```al
// Customer data
DataClassification::CustomerContent
// System metrics
DataClassification::SystemMetadata
// Organizational data
DataClassification::OrganizationIdentifiableInformation
```
### ❌ Don'ts
**Don't Over-Log**
```al
// Too much logging
Telemetry.LogMessage('001', 'Line 1', ...);
Telemetry.LogMessage('002', 'Line 2', ...);
// Don't log every single step
```
**Don't Log Sensitive Data**
```al
// Bad - logging password
CustomDimensions.Add('Password', Password);
// Bad - logging full credit card
CustomDimensions.Add('CreditCard', CreditCardNo);
// Bad - logging personal data unnecessarily
CustomDimensions.Add('SSN', SSN);
```
**Don't Use Generic Messages**
```al
// Bad
Telemetry.LogMessage('001', 'Error', ...);
// Good
Telemetry.LogMessage('SALESORD-E001', 'Failed to post sales order due to credit limit exceeded', ...);
```
## Advanced Telemetry Patterns
### Pattern 1: Telemetry Wrapper
**Prompt**:
```
Create a telemetry wrapper codeunit that simplifies logging for my extension.
Include helper methods for:
- LogStart/LogEnd (with automatic duration calculation)
- LogError (with automatic call stack)
- LogFeatureUsage
- LogPerformanceWarning
Prefix all event IDs with my extension prefix "SALESEXT-"
```
### Pattern 2: Telemetry Constants
**Prompt**:
```
Create a codeunit with constants for all my telemetry event IDs and messages.
This will ensure consistency and make it easier to update messages.
```
### Pattern 3: Contextual Telemetry
**Prompt**:
```
Create a system that automatically adds common context to all telemetry:
- User ID (anonymized)
- Company name (anonymized)
- Extension version
- BC version
- Environment type (Production/Sandbox)
Make this context added automatically to all telemetry calls.
```
## Monitoring and Analysis
### Creating Useful Queries
**Prompt**:
```
Create KQL (Kusto Query Language) queries for Application Insights to analyze:
1. Most common errors in the last 24 hours
2. Average processing duration by order size
3. Feature usage statistics
4. Performance trends over time
5. Error rate percentage
Base the queries on the telemetry we added to the Sales Order Processor codeunit.
```
**Expected Result**:
```kql
// Most common errors in last 24 hours
traces
| where timestamp > ago(24h)
| where message startswith "SALESORD-E"
| summarize ErrorCount = count() by message, customDimensions.ErrorMessage
| order by ErrorCount desc
| take 10
// Average processing duration by line count
traces
| where message == "SALESORD-002"
| extend LineCount = toint(customDimensions.LineCount)
| extend Duration = toint(customDimensions.Duration)
| summarize AvgDuration = avg(Duration) by LineCountBucket = bin(LineCount, 5)
| order by LineCountBucket asc
// Feature usage - discount types
traces
| where message == "FEATURE-DISCOUNT"
| summarize UsageCount = count() by DiscountType = tostring(customDimensions.DiscountType)
| order by UsageCount desc
// Performance warnings
traces
| where message startswith "SALESORD-P"
| summarize WarningCount = count() by Operation = tostring(customDimensions.Operation), bin(timestamp, 1h)
| render timechart
// Error rate percentage
let totalOrders = toscalar(
traces
| where message == "SALESORD-001"
| where timestamp > ago(24h)
| count
);
let failedOrders = toscalar(
traces
| where message startswith "SALESORD-E"
| where timestamp > ago(24h)
| count
);
print ErrorRate = (todouble(failedOrders) / todouble(totalOrders)) * 100
```
## Practice Exercise
Add comprehensive telemetry to this code:
```al
codeunit 50200 "Inventory Adjustment Handler"
{
procedure AdjustInventory(ItemNo: Code[20]; Quantity: Decimal; ReasonCode: Code[10])
var
Item: Record Item;
ItemJnlLine: Record "Item Journal Line";
begin
Item.Get(ItemNo);
ItemJnlLine.Init();
ItemJnlLine."Item No." := ItemNo;
ItemJnlLine.Quantity := Quantity;
ItemJnlLine."Reason Code" := ReasonCode;
ItemJnlLine.Insert(true);
CODEUNIT.Run(CODEUNIT::"Item Jnl.-Post Line", ItemJnlLine);
end;
}
```
**Your Tasks**:
1. Add start/end telemetry
2. Add error handling and logging
3. Track performance
4. Log feature usage
5. Add appropriate custom dimensions
6. Create KQL queries for analysis
## Next Steps
- Learn about [refactoring legacy code](../refactoring) while adding telemetry
- See how [code review](../code-review) can catch telemetry issues
- Explore [testing strategies](../testing) for telemetry code