Problem Space
As Bicep adoption grows, so does the complexity of the environments and teams using it. Without clear authoring practices, Bicep codebases can quickly become inconsistent, hard to maintain, and error-prone. In this post I wanted to share some practical authoring practices and anti-patterns to help you and your team write better Bicep code.
Why Define and Stick to Your Authoring Practices
Defining and following authoring practices ensures:
- Consistency across your codebase and team
- Easier onboarding for new contributors
- Fewer errors and less technical debt
- Improved maintainability and clarity
Below are some key practices and common anti-patterns to consider.
Authoring Practices
1. Folder Structure
Organise your Bicep files in a logical folder structure. For example, group modules by resource type or deployment context. This makes navigation and reuse easier:
├── modules/
│ ├── storage/
│ └── networking/
├── main.bicep
├── parameters/
2. Template Structure
Keep your templates clean and modular. Use modules for reusable components, and keep your main entry point focused on orchestration. Avoid putting everything in a single file.
3. Well Defined Names
Use clear, descriptive names for resources, parameters, and variables. Avoid abbreviations that aren’t widely understood. For example, prefer storageAccountName over saName.
4. Descriptions
Add descriptions to parameters, outputs, and resources. This helps users understand intent and usage:
@description('Configurable name for the application storage account')
param storageAccountName string
5. Comments
Use comments to explain why something is done, not what is done. Avoid echoing the code. Good comments provide context or rationale.
6. Constraints and Metadata
Leverage allowed values, min/max length, and metadata to enforce constraints and provide guidance:
@minLength(3)
@maxLength(24)
@allowed([ 'dev' 'test' 'prod' ])
param environment string
Make sure to use these where there is a specific need, over constraining parameters can equally cause issues.
7. Contracts
Define clear contracts for your modules: what parameters are required, what outputs are provided, and what assumptions are made. Document these in the module and in a README.
8. README
Modules and major orchestrating templates should have a README explaining its purpose and usage. This is invaluable for onboarding and reuse.
9. Bicep Linting
Use Bicep linting as part of your authoring workflow to catch issues early and enforce consistency. Define team linting rules in bicepconfig.json, run lint checks locally during development, and enforce them in CI to prevent low-quality or non-compliant templates from being merged.
10. AI-Generated Templates and Conformance
If you are using AI to generate Bicep, treat your authoring practices as executable guardrails rather than optional guidance.
Start by defining a clear generation contract in your prompt and repository standards:
- Require a specific folder structure and file naming convention
- Require module contracts (documented params/outputs and assumptions)
- Require descriptions, meaningful names, and no dead code
- Require lint-clean output before a template is considered complete
Then enforce conformance automatically in CI:
- Validate formatting and linting on every pull request
- Fail builds when lint rules are violated
- Optionally add policy checks (for example, naming, locations, and SKUs)
- Require human review for architectural decisions, not just syntax correctness
Finally, use a feedback loop. When reviewers find repeated AI mistakes, update your prompt template, lint configuration, and module examples so the next generation cycle improves by default.
Anti-Patterns
1. Premature Abstraction
Don’t create modules or abstractions before you have a real need. Over-abstraction leads to unnecessary complexity and maintenance overhead.
2. Over-Specification
Avoid making every resource parameter configurable if it’s not needed. Too many parameters can confuse users and make templates harder to use.
3. Echo Comments
Comments that simply restate the code add no value. For example:
// Set the storage account name
param storageAccountName string
Instead, explain why a value is needed or any constraints.
4. Dead Code
Remove unused parameters, variables, and resources. Dead code clutters templates and can cause confusion or errors.
5. Generic Copy-Paste Documentation
Avoid boilerplate documentation that doesn’t reflect the actual template. Tailor docs and comments to the specific module or resource.
6. Poorly Defined Names
Names like var1 or resource2 make templates hard to understand. Use meaningful, descriptive names everywhere.
Conclusion
Establishing and following authoring practices for Bicep will help your team deliver infrastructure as code that is robust, maintainable, and easy to understand. Avoid common anti-patterns, and invest in clarity and documentation.
