Developer Standards
These standards outline the expectations for developers at Audacia - covering design, implementation, maintainability, security, and performance.
DevOps & Delivery
Deployment rollbacks should be automated · DEVOPS-02.2 · MUST · DEV
It should be as quick and easy as possible to revert a deployment if needed.
Software updates must be deployed securely · DEVOPS-02.3 · MUST · DEV
Deployments must protect the confidentiality of software and configuration. Use secure, encrypted channels and avoid manual mechanisms.
- Deploy only build artifacts from controlled Build pipelines.
- Store credentials in a Key Vault or Variable Group Secret. Never use plain‑text secrets.
- Use dedicated service principals with minimal RBAC to maintain least-privilage.
- Require pipeline approvals for production releases.
Infrastructure creation should be automated using Infrastructure as Code · DEVOPS-04.1 · MUST · DEV
Use tools such as Terraform or Azure Bicep to automate infrastructure creation and maintenance.
Use YAML pipelines · DEVOPS-01.1 · MUST · DEV/TEST
Always use YAML (YAML Ain’t Markup Language) when creating DevOps pipelines.
Use lower, kebab case for file name, e.g. 'build-app.job.yaml'. · DEVOPS-01.2 · MUST · DEV/TEST
Always use lower, kebab case for file name. For example: build-app.job.yaml
Use Pascal, snake case for IDs, including the type, e.g. 'Job_Build_App'. · DEVOPS-01.3 · MUST · DEV/TEST
Always use Pascal, snake case for IDs, including the type. For example: Job_Build_App
Include a blank line between steps. · DEVOPS-01.4 · MUST · DEV/TEST
Always include a blank line between steps.
steps:
- task: PowerShell@2
inputs:
targetType: inline
script: Write-Host "Hello world"
- task: PowerShell@2
inputs:
targetType: inline
script: Write-Host "Goodbye world"Batch changes when using the CI trigger. · DEVOPS-01.5 · MUST · DEV
Always batch changes when using the CI trigger.
Include an approval gate for deployments to customer-facing environments. · DEVOPS-01.6 · MUST · DEV/TEST
Always include an approval gate for deployments to customer-facing environments. View documentation
Use folders to group pipelines. · DEVOPS-01.10 · MUST · DEV/TEST
Always use a sensible folder structure for grouping pipelines (e.g., /deployment or /testing)
Include an approval gate for deployments to internal environments. · DEVOPS-01.7 · SHOULD · DEV/TEST
It is strongly recommended that you include an approval gate for deployments to internal environments.
Split stages and jobs into their own file. · DEVOPS-01.8 · SHOULD · DEV/TEST
It is strongly recommended that you split stages and jobs into their own file.
Include explicit 'Use Node' and 'Use .NET' steps. · DEVOPS-01.9 · SHOULD · DEV/TEST
It is strongly recommended that you include explicit Use Node and Use .NET steps.
# Use Node.js ecosystem v1
# Set up a Node.js environment and add it to the PATH, additionally providing proxy support.
- task: UseNode@1
inputs:
#version: '10.x' # string. Version. Default: 10.x.
#checkLatest: false # boolean. Check for Latest Version. Default: false.
#force32bit: false # boolean. Use 32 bit version on x64 agents. Default: false.
# advanced
#retryCountOnDownloadFails: '5' # string. Set retry count when nodes downloads failed. Default: 5.
#delayBetweenRetries: '1000' # string. Set delay between retries. Default: 1000.# Use .NET Core v2
# Acquires a specific version of the .NET Core SDK from the internet or the local cache and adds it to the PATH. Use this task to change the version of .NET Core used in subsequent tasks. Additionally provides proxy support.
- task: UseDotNet@2
inputs:
#packageType: 'sdk' # 'runtime' | 'sdk'. Package to install. Default: sdk.
#useGlobalJson: false # boolean. Optional. Use when packageType = sdk. Use global json. Default: false.
#workingDirectory: # string. Optional. Use when useGlobalJson = true. Working Directory.
#version: # string. Optional. Use when useGlobalJson = false || packageType = runtime. Version.
#includePreviewVersions: false # boolean. Optional. Use when useGlobalJson = false || packageType = runtime. Include Preview Versions. Default: false.
# Advanced
#vsVersion: # string. Compatible Visual Studio version.
#installationPath: '$(Agent.ToolsDirectory)/dotnet' # string. Path To Install .Net Core. Default: $(Agent.ToolsDirectory)/dotnet.
#performMultiLevelLookup: false # boolean. Perform Multi Level Lookup. Default: false.Parameters must be explicitly and unambiguously named · DEVOPS-03.1 · MUST · DEV/TEST
The name must clearly describe the parameter’s purpose without requiring additional context, ensuring that anyone can reliably identify and reference it.
Each parameter must be clearly and individually declared · DEVOPS-03.2 · MUST · DEV/TEST
To support clarity and prevent misinterpretation, each parameter must be declared on its own rather than embedded in compact or nested structures.
Parameter definitions must include essential metadata · DEVOPS-03.3 · MUST · DEV/TEST
All parameters must provide the necessary context to function predictably. This includes core attributes such as identifier, type, and default behaviour / value.
Parameter names must follow consistent naming conventions · DEVOPS-03.4 · MUST · DEV/TEST
Use a consistent casing convention (e.g. camelCase) for parameter names to ensure readability and familiarity across pipelines and projects.
Variables must be defined in a controlled and traceable manner · DEVOPS-03.5 · MUST · DEV/TEST
Avoid scattering variable definitions across pipeline stages. Use centralized declarations (e.g., variable groups, templates) to promote transparency and version control.
Secure variables must be stored in approved secret management tools · DEVOPS-03.6 · MUST · DEV/TEST
Sensitive values (e.g., connection strings, API keys) must be stored using secure mechanisms such as:
- Azure DevOps Library Secure Files
- Secret variable within a variable group
- Azure Key Vault integration
Variable scope must match usage context · DEVOPS-03.7 · MUST · DEV/TEST
Define variables at the most appropriate scope:
- Pipeline-level for global use
- Stage/job-level for isolated logic
- Environment-level for deployment scenarios
Avoid overly broad scoping, which can lead to unexpected overrides or conflicts.
Variable names must use a consistent format · DEVOPS-03.8 · MUST · DEV/TEST
Use a clear naming convention such as UPPER_SNAKE_CASE or camelCase, depending on the team standard. Prefix values logically where relevant (e.g., dbConnectionString, testTimeoutMinutes).
Common variables must be centralized for reuse · DEVOPS-03.9 · MUST · DEV/TEST
Frequent or shared values must be placed in reusable variable groups or YAML includes. This minimizes duplication and improves maintainability.
Quality
Static code analysis warnings must fail the build · QUAL-02.2 · MUST · DEV
NOTE On legacy codebases this can be a target that is being worked towards.
REST APIs must be documented using the OpenAPI standard · REST-01.1 · MUST · DEV
The following things must be included in the documentation:
- Request types
- Response types
- Response codes
The appropriate HTTP verb must be used · REST-02.1 · MUST · DEV
Depending on the operation being performed by the API, the following HTTP verbs must be used:
- GET: Read-only operation that returns data
- PUT: Write operation that updates existing data
- POST: Write operation that creates new data
- DELETE: Write operation that removes existing data
It is important to note that GET, PUT and DELETE endpoints should be idempotent. If an endpoint is not idempotent (e.g. as well as updating/deleting/retrieving data, it also creates some data) then POST should be used.
Routes should be nouns · REST-02.2 · SHOULD · DEV
Nouns should primarily be used when building routes.
Simple routes must be based on nouns rather than verbs, e.g. /customers rather than /getCustomers, where the nouns are ‘resources’.
An exception to this rule would be that verbs can be used to represent a specific action to be taken on a resource, usually being specified at the end of the route, e.g. movies/{id}/play.
Routes must contain the IDs they act upon · REST-02.3 · MUST · DEV
Any endpoint that is performing an action on a specific item must specify the ID in the route, e.g. /customers/{id}. This applies to GET, PUT and DELETE requests.
If resources have children then it is often appropriate to reflect this in the route. For example, if each customer has a collection of addresses, then a good route would be /customers/{id}/addresses, where the {id} placeholder is the ID of the customer.
Routes should not have a depth greater than three · REST-02.4 · SHOULD · DEV
Limit the depth to which child resources are nested in a route to three levels, wherever possible.
For example, one/{idOne}/two/{idTwo}/three/{idthree} would be appropriate however one/{idOne}/two/{idTwo}/three/{idThree}/four should be restructured, possibly by making the resource two the root of the route.
Controllers must contain the endpoints for a single resource · REST-02.5 · MUST · DEV
Controllers must contain the endpoints for a single resource (following the single responsibility principle).
For example, endpoints at the root of customers (GET /customers/{id}) would be in the CustomersController. Endpoints for child resources should be in their own controller, meaning GET customers/{id}/addresses would be in the AddressesController.
Route naming must be consistent · REST-02.6 · MUST · DEV
Kebab-cased urls are preferred, e.g. /order-items rather than /orderItems.
When defining routes for a controller avoid using [Route(“[controller]”)], as this couples the url to the name of the controller. It is therefore possible to break clients by renaming a controller.
Successful GET requests must return data · REST-03.2 · MUST · DEV
- If the GET request is for a specific ID (i.e.
/customers/{id}), the response body should be a single object, which may be empty. - If the GET request is not for a specific ID (i.e.
/customersor/customers?city=Leeds), the response body should be an array containing zero or more objects.
Successful POST requests must include a location header · REST-03.6 · MUST · DEV
A location header should be included set to the url from which the resource can be obtained in future (i.e. the appropriate GET endpoint). ASP.NET Core provides the CreatedAtAction method to do this.
EXCEPTION This does not apply if the POST endpoint is for a search (e.g. where there are too many filters to capture in a query string so GET cannot be used).
Successful POST requests should return the data that has been created · REST-03.8 · SHOULD · DEV
The object created (including its ID) should be returned in the response body. If the object is large, or if it is infeasible to return the entire object, then the ID should be returned.
All API endpoints that receive data must validate that input · REST-04.1 · MUST · DEV
For example, validating the content of a request body, or the ID in a query string.
Validation errors should be returned as a 400 (Bad Request) status code · REST-04.2 · SHOULD · DEV
A validation error is an error on behalf of the client, and there a code in the 4** range is appropriate. Generally speaking a 400 status code is the best choice.
Error HTTP status codes must contain information about the error returned · REST-04.3 · MUST · DEV
In the case of a non-2** status code being returned, a response body must contain information about the error.
The structure of the error response must be consistent across the API, i.e. all responses should have a body with the same structure. The following points should be considered:
- Return a list of errors rather than a single error
- Return an error code as well as a message
Conventional Commits must be used for commit messages · QUAL-04.1 · MUST · DEV/TEST
Commit messages must follow the Conventional Commits specification.
It is strongly recommended that projects at least use types from the specification to communicate the main purpose of the commit, e.g.:
feat:for new featuresfix:for bug fixesdocs:for documentation changesrefactor:for refactoring- etc. (any type from the specification can be used)
Projects can optionally go beyond just using the type and follow the full specification, which includes additional features such as:
- Including a scope after the type, e.g. the scope
apiafter a feature would befeat(api): - Communicating breaking changes by appending a
!after the type/scope, e.g.feat!: - Including a footer
Commit messages must including work item number · QUAL-04.2 · MUST · DEV/TEST
The work item number (e.g. PBI or Bug) must be added to the end of the commit message. For example:
fix: added phone number validation #xxxx, where #xxxx is the card related to the commit
A branch policy must be set on main development branches · QUAL-05.1 · MUST · DEV/TEST
Code reviews must be enforced by setting a branch policy on the main development branches (e.g. dev and/or main) with the following settings:
- Require a minimum of at least 1 reviewer (an individual project can require more than 1 reviewer if desired).
- Do not allow users to approve their own changes (unless the minimum number of reviewers is more than 1).
- ‘Reset code reviewer votes when there are new changes’ should be selected.
- ‘Check for linked work items’ should be required.
- ‘Check for comment resolution’ should be required.
- ‘Limit merge types’ should generally be set to ‘Basic merge (no fast-forward)’.
- ‘Build validation’ should be set for the relevant build pipeline(s) to ensure that the pull request doesn’t break the build, with the expiration set to immediately.
Projects can diverge from this policy provided it makes the policy stricter rather than more relaxed. For example, requiring more than 1 reviewer.
Documentation
Delivery team roles and responsibilities must be documented · DOC-01.1 · MUST · DEV/TEST
These role and responsibilities can be captured as a RACI matrix, or something less formal if preferable.
Client key contacts must be documented · DOC-01.2 · MUST · DEV/TEST
Important information to capture includes each person’s contact details, role and decision-making authority on the project.
An overview of each delivery phase must be documented · DOC-01.3 · MUST · DEV/TEST
At a minimum, the following information must be included:
- List of each phase delivered
- Delivery dates
- High-level summary of what was delivered in each phase.
EXCEPTION This is not applicable to projects that have not had multiple delivery phases.
README files must document the technologies used within the repository · DOC-04.2 · MUST · DEV/TEST
The main technologies used and any pre-requisite software that must be installed must be listed in the README: for example .NET 10, Node.js 20, Playwright.
README files must document how to use the repository locally · DOC-04.3 · MUST · DEV/TEST
Application Code
- Document any required installs, such as SSL Certificates
- Document any configuration files such as
appsettings.Development.jsonornuget.config - Document how to setup local development, including any local databases
- Document how to run the application locally, explaining any npm scripts and any command-line options or parameters that must be provided
- Document any additional applications (such as IDEs, or IDE extensions/plugins) which are needed for debugging etc.
Automated Tests
- Document any required installs
- Document any credentials required to run the tests and the file they should be added to. For example, contributors should populate
cypress.env.jsonfrom the UI Cypress Tests record in Keeper. - Document how to run the tests locally, explaining any npm scripts and any command-line options or parameters that must be provided
- Document any additional applications which may be used. For example, Playwright Test for VSCode by Microsoft.
A glossary of terms and acronyms used within a project must be provided · DOC-02.2 · MUST · DEV/TEST
The architecture of a system must be documented · DOC-03.1 · MUST · DEV
This should be documented in the form of one or more architecture diagrams.
Architecture decision records (ADRs) must be written to record key technical decisions · DOC-03.2 · MUST · DEV/TEST
ADRs can be included in the main project wiki or in the same repo as the source code (and linked to from the main wiki).
The hosting location and access must be documented · DOC-03.3 · MUST · DEV
Possible hosting locations include:
- Audacia CSP hosted
- Client hosted
- Third-party hosted (the third-party must be specified)
Some examples of access that should be documented are:
- Guest accounts in an external Azure tenant
- Separate client-provided accounts
- Any specific access requirements, like a VPN or white-listed IP address
Environments and system URLs must be documented · DOC-03.4 · MUST · DEV
NOTE This information must be recorded in Olympus.
Non-functional requirements must be documented · DOC-03.5 · MUST · DEV/TEST
Examples of non-functional requirements are:
- Performance and response times
- Accessibility
- Device testing requirements (these should be captured in the Test Strategy)
- Logging and observability
Deployment and release processes must be documented · DOC-03.6 · MUST · DEV
The kind of processes that should be documented include:
- CI/CD pipelines
- Health checks
- Release windows, i.e. any days/times agreed by the client
- Manual processes (where applicable)
- Rollback procedure
The branching strategy must be documented · DOC-03.7 · MUST · DEV/TEST
This should include the overall strategy, the reasoning behind it, and any specific details such as naming conventions, e.g. release/.
Disaster recovery processes must be documented · DOC-03.8 · MUST · DEV
The following items must be included:
- Agreed Recovery Time Objective (RTO) and Recovery Point Objective (RPO), or if none has been agreed a statement to this effect together with a reference to when this decision was made
- Details of any processes to follow beyond restoring data to a known point, e.g. any manual failover tasks or fallback to paper-based processes
- Details of what backups are taken, at what frequency, to what location, and who owns them
External resources must be listed and signposted · DOC-03.9 · MUST · DEV/TEST
For example, Postman collections or JMeter scripts.
The use of feature switches must be documented · DOC-03.10 · MUST · DEV
NOTE This is only applicable if feature switches are actually in use.
A troubleshooting quickstart guide must be provided · DOC-03.12 · MUST · DEV
Include information that will be useful for anyone triaging or debugging an issue, such as where logs are located in each environment.
Observability
All hosting environments that incur a cost must have the expected cost documented · HC-01 · MUST · DEV
Each project must maintain a “Hosting Costs” page in the project wiki. This page must either:
- Detail the expected monthly cost per environment, with the date of last review.
- Link directly to the cloud provider’s cost management view for each environment (e.g. Azure Cost Management, AWS Budgets).
All environments must have a budget configured to alert on cost overruns · HC-02 · MUST · DEV
Configure a budget for each environment with alerts in the cloud provider’s cost management service. Alerts must notify the project team via agreed channels on cost overruns. Budget amounts must be kept up to date as resources are added, scaled or deprovisioned.
Tags must be used on the resource group to designate the environment and the customer · HC-03 · MUST · DEV
Apply tags on the resource group to enable accurate cost allocation. At a minimum, include:
- environment: e.g. QA, UAT and Prod
- customer: the client name or identifier
All budgets must be approved prior to creation or change · HC-04 · MUST · DEV
Budgets for any environment must be approved by IT before they are created, increased, or changed.
NOTE Approval of hosting costs for specific environments (e.g. Dev/QA) does not imply approval for other environments (e.g. UAT/Production), which may require separate formal sign-off.
Use structured logging · LOG-01.1 · MUST · DEV
Consistency and readability are crucial in our logs. Providing strong discoverability speeds up development and aides in diagnosing problems.
Unstructured logging example (❌)
console.log("User logged in at 2024-08-02T12:34:56Z");Structured logging example (✅)
console.log(JSON.stringify(new Log(
timestamp: "2024-08-02T12:34:56Z",
level: "info",
message: "User logged in",
userId: 12345
));Messages are clear and concise · LOG-01.2 · MUST · DEV
Each log message should be one or two sentences, which relay information relating to the level of the log.
e.g.
| Level | Message |
|---|---|
| Information | Entry: Attempting to save {numberOfInvoices} Invoices. |
| Information | Successfully saved Invoice {invoiceName}, Id: {invoiceId}. |
| Error | Error thrown while saving Invoice {invoiceName} due to a missing payment line. |
| Information | Exit: Successfully saved {numberOfSuccesses}/{numberOfInvoices} Invoices. |
Messages are templated · LOG-01.3 · MUST · DEV
Use templates for log messages instead of interpolation to ensure consistency and parsability.
What bad looks like (❌)
// Bad
_logger.LogInformation($"Processing Payment {paymentIdToProcess}");This example creates a log from a simple string, making it less structured.
What good looks like (✅)
// Good
_logger.LogInformation("Processing Payment {paymentIdToProcess}", paymentIdToProcess);This version uses a templated string, resulting in an enriched log with a custom property named paymentIdToProcess. This allows for better querying and analysis in the logging back-end.
Advantages of Templated Strings
-
Consistency: Ensures that the overall structure of each message remains consistent.
-
Queryability: Allows for specific and general searches. For example, you can query for:
“Processing Payment {paymentIdToProcess}”to get all occurrences of this log.“Processing Payment 412”to find specific instances.
-
Enhanced Context: You can pass complex objects instead of primitive types, enabling richer log details. Ensure these objects do not include Personal Identifiable Information (PII) unless approved by the client.
For example, if using Application Insights, you can search for “paymentIdToProcess” or “paymentIdToProcess 412” to pinpoint specific logs more effectively.
Use meaningful and engineer-friendly messages · LOG-01.4 · MUST · DEV
State the implication of the codes execution before or after the log line and the action an engineer would need to take (if any).
For example:
// Bad - vague with no explanation:
_logger.LogError("An error occured while trying to process the Payment {paymentId}", paymentId);
// Good - says specifically why:
_logger.LogError("Payment {paymentId} failed during processing because the customer's payment provider was not configured.", paymentId);Avoid re-using user-facing error messages. Logs are read by engineers, the steps of which one would take to resolve the problem would differ greatly. “Please contact your system administrator” would help the user but not an engineer.
Never log sensitive data unless absolutely necessary · LOG-01.5 · MUST · DEV
Information such as passwords, application secrets (e.g. API Keys) and Personal Identifiable Information (PII) must only be logged if an agreement is in place with the client.
Logging sensitive data must be subject to agreements with the client · LOG-01.5.1 · MUST · DEV
Such an agreement must be made with security considerations regarding access-control and the right to be forgotten.
Store logs in secure & access-controlled ways · LOG-02 · MUST · DEV
Logs must be stored using secure solutions to protect client data. Application insights is a common option for this, but engineers are not restricted to this, so long as the alternative solution has access-control.
Use a consistent logging solution · LOG-03 · SHOULD · DEV
Use the same logging provider for the front-end, back-end or data layers respectively.
e.g. For a .NET backend, if one class implements logging via the ILogger Microsoft.Extensions.Logging , then other classes on the backend should use the same logging provider.
Whichever logging provider the UI uses (e.g. Sentry) then this should be used for all logging on the front end.
Whether logs are from the UI or the server, they should be viewable in the same monitoring solutions (e.g. Application Insights).
Use appropriate logging · LOG-04 · MUST · DEV
Use appropriate log levels · LOG-04.1 · MUST · DEV
Logs must have an assigned log level which describes how that log is intended to provide value.
| Level | Usage | Example |
|---|---|---|
| Trace | Detailed information that will only be used when diagnosing bugs. | LogTrace(“Starting search for disabled users.”, args[…]) |
| Debug | Information that will be used when diagnosing bugs in debug mode and not production. | LogDebug(“Deserialised JSON from {jsonString} to {object}”, args[…]) |
| Information | Highlight the progress of the executing code. | LogInformation(“Attempting to rename object with Id {id} from {oldName} to {newName}.”, args[…]) |
| Warning | Events which are potentially, or could lead to, harmful situations | LogWarning(“Using a default Payment Provider as the value of one wasn’t provided.”, args[…]) |
| Error | Error event occurred which might allow the code to continue running | LogError(“Could not find object with Id {id} and name {oldName} in the database.”, args[…]) |
| Critical | Error events which are severe and may halt the executing code. | LogCritical(“Connection string is missing for database context {databaseContext}.”, args[…]) |
Avoid excessive logging that may cause performance degradation · LOG-04.2 · MUST · DEV
Logging should be performed judiciously throughout a user workflow to ensure that observability is seamlessly integrated without impacting performance. Excessive logging can lead to performance degradation and increased costs. Techniques such as Adaptive Sampling can automatically remove redundant log lines, which may result in loss of effort when too much logging has been implemented.
Debug Logs
If a log line is valuable for debugging but not essential for the production environment, consider using the Debug level. This helps keep production logs concise while still providing detailed information for development and troubleshooting.
Projects must have a client-agreed retention policy · LOG-04.3 · MUST · DEV
Every project should establish a log retention policy agreed upon with the client. This policy defines how long logs will be stored before they are archived or deleted. This ensures compliance with legal, operational, and business requirements.
Ensure log storage compliance with data location policies · LOG-04.4 · MUST · DEV
Logs must be stored in compliance with internal policies and applicable legislation regarding data location. Specifically, storing logs in non-UK/EU data centers may not be compliant and could lead to legal and regulatory issues. Always verify and adhere to data storage regulations relevant to your project to ensure compliance.
Don't repeat messages · LOG-04.5 · SHOULD · DEV
Each log message should contain information specific to the logic being executed around it. Ensure that each log line includes unique details that provide context for the execution.
Repeated log messages can hinder an engineer’s ability to find useful information during searches. Avoid duplication to maintain the effectiveness of your logging system.
Signpost significant application events · LOG-04.6 · SHOULD · DEV
Logging should be added to important workflow moments. This leaves breadcrumbs which can later be used to diagnose failure points in the application.
| Event | Example Message |
|---|---|
| Major code branching | ”Processing Payment 5 via the STRIPE strategy as a STRIPE signature was found.” |
| Domain events that will affect application behaviour | ”The PaymentProviderSelection feature switch has been turned on, the payment page now allows users to select their provider.” |
| The entry point of intensive processes | ”Entry: User 0f153eb6-ec66-4de6-9642-9dc2f8c3bfa9 initiated bulk processing of 100 payments.” |
| The exit point of intensive processes | ”Exit: Finished processing 100 payments successfully.” |
| The entry point of a brittle process | ”Attempting to send a payment process request to the STRIPE API.” |
Automated Testing
Unit tests must be written to test application code · UT-01.1 · MUST · DEV
These basic rules should be followed:
- Unit tests should cover core business logic and validation, including edge cases
- Unit tests generally are not needed for simple CRUD functionality
- Communication is needed with test engineers to ensure effort is not duplicated among different kinds of automated test
- Unit testing is not a substitute for manual developer testing
Unit tests within a system must use a consistent naming convention · UT-01.2 · MUST · DEV
All unit test methods should follow the same naming convention. Each project can have it’s own conventions so long as it’s adequately documented.
Unit tests must have a descriptive name that documents the business logic under test · UT-01.3 · MUST · DEV
Writing unit tests as documentation helps to ensure that when the project is handed over to support, or when different teams are delivering new phases of work, there is clear, reliable, and up-to-date documentation of the code’s intended functionality. This facilitates smoother transitions and reduces onboarding time for new team members.
Unit tests must assert against the behaviour in the method name · UT-01.4 · MUST · DEV
In other words, don’t have a test method name that says one thing, and then assert on something else within the method.
Unit tests must be atomic · UT-01.5 · MUST · DEV
Each test must be independent of other tests, i.e. a test should not rely on any other test already having run.
Build validation must be in place to fail the build if unit tests fail · UT-01.6 · MUST · DEV
There should be a step in any pipeline that builds the software to also run unit tests.
Unit tests must not use data that may change between test runs · UT-01.7 · MUST · DEV
For example:
- Randomly generated data
DateTime.Now/DateTime..Today
EXCEPTION Random data can be used if it does not impact the outcome of the test, e.g. a mandatory property that is not needed by the behaviour under test.
Unit tests should not mock objects that are under the control of the system under test · UT-01.8 · SHOULD · DEV
For example, you should generally not mock other classes within your codebase, but should mock things like external APIs, message queues, and email clients.
Unit tests should not re-use domain logic within tests · UT-01.9 · SHOULD · DEV
Generally speaking, when business logic code is changed, unit tests that test that logic should fail. Re-using domain logic in a test can mean that happen, and therefore should be avoided.
Unit tests should use realistic test data · UT-01.10 · SHOULD · DEV
Using realistic data in unit tests can help to document the behaviour under test and ensures that the tests are as close as possible to real-world usage.
NOTE The Audacia.Seed library can be used to help the structured, reusable generation of test data.
User Experience
Reuse Proven UI Patterns · UX-01.1 · MUST · DEV
Use widely adopted UI patterns for common flows such as sign-up, login, password reset, onboarding, etc. Avoid novelty in routine interactions unless it’s tied to product differentiation. Leveraging existing UX patterns reduces user learning curves and speeds up delivery. Reference aggregators like Mobbin or PageFlows for examples.
✓ Use centred forms, clear buttons, and familiar field layouts.
✓ Reference competitor flows where users already feel comfortable.Set UX Goals Explicitly · UX-01.2 · MUST · DEV
Define a clear, outcome-focused goal for every user flow. Avoid focusing on UI components in isolation. Reframe goals as questions to guide better UX decisions.
✗ "Build a sign-up form with two text fields and a button."
✓ "How can we make sign-up as fast and error-proof as possible?"This practice ensures the design stays focused on real outcomes like conversion or task completion.
Optimize for Edge Cases · UX-01.3 · MUST · DEV
Anticipate user errors and distractions. Build defences and guidance into the UI at both field and flow levels.
✓ Provide instant feedback on errors (e.g. mismatched passwords).
✓ Confirm input accuracy before submission (e.g. "confirm email").
✓ Display clear error messages: "We couldn't save your changes", not "500 Internal Server Error".Good UX accounts for the 80% happy path and the 20% that causes real frustration.
Use Established Layout & Interaction Conventions · UX-01.4 · MUST · DEV
Stick to UI conventions users already know.
✓ Logo top-left
✓ Primary CTA bottom-right or centred
✓ Forms in vertical layout with visible labels
✓ Responsive design for all viewportsAvoid design award aesthetics that compromise usability. Favour familiarity and clarity over originality unless deliberately differentiating.
Keep Visual Design Simple · UX-01.5 · SHOULD · DEV
Use a restrained palette: one primary, one secondary, and one accent colour. Avoid visual noise. Use tools like Coolors for consistent colour schemes.
✓ Use consistent button styles across the app.
✓ Avoid gradients, excessive shadows, or non-functional animations.Simple visuals reduce user distraction and improve focus.
Prioritize Clarity in Language · UX-01.6 · MUST · DEV
Use language familiar to your audience. Avoid technical jargon or developer-oriented messages.
✗ "Validation error: token mismatch"
✓ "You’ve been logged out. Please sign in again."Write content as if explaining to a non-technical friend. Error states especially should offer solutions, not confusion.
Learn from Adjacent Domains · UX-01.7 · SHOULD · DEV
When designing new or uncommon flows (e.g. collecting sensitive data), look at adjacent industries:
✓ Study mortgage, tax, and legal apps for patterns around trust and risk.
✓ Identify how these platforms build credibility and reduce anxiety.Use this insight to design flows that are clear, empathetic, and trustworthy.
Leverage AI for UX Testing · UX-01.8 · SHOULD · DEV/TEST
Use AI tools like ChatGPT for usability feedback. Simulate user perspectives to reveal blind spots. Example prompts:
✓ "Critique this sign-up flow: where could users get stuck?"
✓ "Act like a distracted user—what's confusing in this screen?"
✓ "How do top SaaS products design this flow?"AI feedback isn’t final, but it helps prioritize areas for further testing or validation.
Choose Metrics That Matter · UX-01.9 · MUST · DEV
Define clear success metrics per flow: conversions, drop-off rate, completion time, or task success.
✓ Track user behaviour via tools like Amplitude or Hotjar.
✓ Use metrics to challenge assumptions and identify improvement areas.Measuring UX makes quality tangible, allowing for iteration based on real-world feedback.
Follow Jakob's Law · UX-01.10 · SHOULD · DEV
Users spend most of their time on other sites. This means that users prefer your site to work the same way as all the other sites they already know.
Follow Jakob’s Law by defaulting to use what already works. Innovate only on your core differentiators.
✓ Use standard flows to train users via familiarity.
✓ Focus your innovation where it enhances value, not on aesthetics.Efficient UX is about speed, clarity, and impact—not novelty.
Security & Availability
Applications must use Mailtrap for emails in test environments · DS-01 · MUST · DEV/TEST
Applications must use Mailtrap for emails in test environments
EXCEPTION: This requirement does not apply if the client has their own service or process in place for handling test emails.
Data must be anonymised when moved between environments · DS-02 · MUST · DEV/TEST
Sensitive data must be anonymised before being transferred between environments (e.g., from production to development or test).
EXCEPTION: This requirement may be waived if the data is essential for reproducing a specific issue, provided there is a documented process in place to delete the data from the destination environment once it is no longer required.
Sensitive data must be stored securely · DS-03 · MUST · DEV/TEST
Sensitive data includes credentials, API keys, connection strings and certificates. Store only in approved secret stores and encrypted services. Never commit to source control or place in plain text in config.
- Use a managed cloud secret store for secrets, keys and certificates (e.g. Azure Key Vault or AWS Secrets Manager).
- Store pipeline secrets in your platform’s secure secret management (e.g. Azure DevOps Variable Groups or Github Actions secrets). Restrict access via RBAC and least privilege. See DEVOPS-03.6.
- For local development, store
.envsecrets in Keeper as a “Secure Note”. Do not share as plain text.
A database backup strategy must be implemented in production environments · SEC-03.1 · MUST · DEV
For production environments where Audacia is responsible for a system’s infrastructure, a database backup strategy must be agreed with the client and documented in the project’s wiki.
Any backup strategy must enable compliance with any applicable data privacy legislation · SEC-03.2 · MUST · DEV
If a database contains personally identifiable information, a process must be agreed with the client to ensure that a user’s right to be forgotten under GDPR legislation is honoured. In the event of a database backup being recovered, any user who has requested to be forgotten must have their information removed.
The scope of the penetration test must be defined clearly in advance · PEN-01 · MUST · DEV/TEST
Define the scope explicitly before testing begins, specifying systems, applications, and components to be tested.
Recognised scope types include:
- External Network Testing
- Internal Network Testing
- Web Application Testing
- API Testing
- Cloud Infrastructure Testing
- Social Engineering (only if explicitly agreed)
The penetration test must have formal sign-off from the System Owner and Relevant Stakeholders before it begins · PEN-02 · MUST · DEV/TEST
An agreed contract or Statement of Work (SoW) from the testing provider must be in place and cover: scope, methodology, test windows, escalation paths, impact management, and legal statements.
Testing should be performed on a representative environment · PEN-03 · SHOULD · DEV/TEST
Prefer a non‑production environment that mirrors production configuration (e.g. Azure Front Door, Web Application Firewall). Avoid testing in production unless absolutely necessary and explicitly approved.
System access to conduct the penetration test must be controlled and monitored · PEN-04 · MUST · DEV/TEST
Grant the least privilege required for the duration of testing. Access must be time‑bound and revoked immediately after testing concludes. Maintain audit logs of access and tester activity, and rotate any credentials or tokens post‑test.
Penetration testers must not have access to production data · PEN-05 · MUST · DEV/TEST
Do not provide testers with access to live production data. If production must be used, ensure no production data is present, or anonymise sensitive data to an acceptable standard. After testing, reset or roll back changes and revoke access.
Penetration testers must provide a formal report of findings and remediation recommendations · PEN-06 · MUST · DEV/TEST
Require a formal report including scope and methodology, evidence of findings with severity ratings, impacted assets, reproducible steps, and clear remediation recommendations.
Use long-term support framework versions · SEC-01.1.1 · MUST · DEV/TEST
Applications must target long-term support (LTS) versions of major frameworks (e.g. .NET or Angular).
Using LTS versions of frameworks improves an applications security, as LTS releases receive security patches and updates for a longer period than standard term support versions. For .NET applications, Microsoft provides LTS releases with 3 years of patching support.
Avoid the use of libraries with known vulnerabilities · SEC-01.1.2 · MUST · DEV/TEST
Applications must not utilise external libraries with known vulnerabilities (e.g. older versions of jQuery).
The use of external libraries should be carefully monitored, reviewed, and virus scanned in order to reduce the risk of malicious code injection (for example, cross-site scripting injection).
Use secure package versions · SEC-01.1.3 · SHOULD · DEV/TEST
Applications must maintain the security and reliability of all libraries and packages by upgrading to the latest secure version.
If a vulnerability is discovered, then it should be upgraded to the latest, patched version as soon as one becomes available.
Vulnerabilities can propagate through indirect dependencies or outdated packages - any packages which cannot be upgraded to secure versions must be recorded as a risk within the project tracker.
Use strict code compilation · SEC-01.1.4 · SHOULD · DEV
All code must be compiled to the highest warning level available to the compiler.
These warnings should be investigated to ascertain a severity, before they have the potential to become logic errors or security vulnerabilities, where critical vulnerabilities and warnings are actioned as soon as possible.
All application credentials must follow the Password and Authentication Policy · SEC-01.2.1 · MUST · DEV/TEST
To ensure password complexity, passwords must adhere to Audacia’s ‘Password and Authentication Policy’
Application credentials must not be re-used · SEC-01.2.2 · MUST · DEV/TEST
Unique, auto-generated passwords should be used to ensure the same password cannot be reused for accounts across multiple applications.
Un-encrypted credentials must not be stored in source control · SEC-01.2.3 · MUST · DEV/TEST
All credentials (not limited to API keys, default application logins or database config) must not be stored in source control.
If encrypted credentials are stored in source control, the necessary decryption key(s) must stored securely elsewhere.
Passwords must be salted/hashed before being stored · SEC-01.2.4 · MUST · DEV
Passwords must never be stored in plaintext. If an application manages authentication itself, any passwords must be salted and hashed before being stored.
Passwords or personal information must be obfuscated before being logged · SEC-01.2.5 · MUST · DEV
If any data containing personal information or a password is being logged, this should be obfuscated (replaced with ”****” or an empty string) before being logged. If information about who performed a certain action is to be logged, instead use database-generated user ID or similar identifier.
Emails must not send information to external sources · SEC-01.2.6 · MUST · DEV
Test emails must use one of the following approaches
- Use a domain that is recognised as invalid such as
@example.comor.test - Use your Audacia email with plus addressing so create alternative emails. For example
audaciaemail+{stringvalue}@audacia.co.uk
Validate, encode or escape user input to prevent cross-site scripting (XSS) · SEC-01.3.1 · MUST · DEV/TEST
Any data entered by a user must be treated as untrusted.
All untrusted input must be validated, escaped or encoded appropriately when being rendered in a HTML page as this defends against cross-site scripting (XSS).
Server-side input validation must be performed,to ensure that malicious or malformed data is not processed. This is in addition to client-side validation, which can be by-passed.
Automated API tests should be written for input validation
Functional API tests can validate business logic rules around field length, required fields and expected data types faster than UI tests which need to login or navigate to the form or page under test.
Use request verification tokens to prevent cross-site request forgery (CSRF) · SEC-01.3.2 · MUST · DEV
Applications must use request verification tokens (anti-forgery tokens) to validate all requests.
Request verification tokens protect against data tampering i.e. if an attacker modifies form data, the token validation will fail, rejecting the request.
Utilise parameterized queries to prevent against SQL injection · SEC-01.3.3 · MUST · DEV
Always use parameterized queries if executing SQL directly from code (although an ORM or, failing that, a stored procedure with no unparameterized dynamic SQL should be preferred for any data access).
Applications should utilise virus scanning · SEC-01.3.4 · SHOULD · DEV
All systems that use file upload functionality, should have anti-virus configured and tested.
File upload architectures must be documented · SEC-01.3.5 · MUST · DEV
The anti-virus configuration and file upload architecture should be documented in the projects Architectural Decision Records.
Do not serialized objects from untrusted sources · SEC-01.3.6 · MUST · DEV
Applications must not accept serialized objects from untrusted sources to prevent against remote code execution attacks.
If this is not possible, one or more of the following strategies should be adopted to avoid insecure deserialization:
- Implementing integrity checks such as digital signatures on any serialized objects to prevent hostile object creation or data tampering.
- Enforcing strict type constraints during deserialization before object creation as the code typically expects a definable set of classes.
- Log deserialization exceptions and failures, such as where the incoming type is not the expected type, or the deserialization throws exceptions.
Adopt rate limiting on all endpoints · SEC-01.4.1 · MUST · DEV
Applications must employ rate limiting on all endpoints to mitigate against brute force attacks, prevents server overload and DoS.
If the system utilises a Web Application Firewall (WAF) then rate limiting should be implemented in the WAF, the AspNetCoreRateLimit NuGet package provides a coded alternative.
Rate limiting decisions should be documented · SEC-01.4.2 · SHOULD · DEV
Weather all requests made to an API are rate limited or limits are applied to each API URL or HTTP verb and path, this should be documented in the projects Architectural Decision Records.
Access and authorisation must be restricted · SEC-01.5.1 · MUST · DEV
Features and system areas must be protected by roles, that give users the permissions to access only required areas/information.
If necessary authorization should be applied to specific data to prevent ID enumeration.
API endpoints must be authenticated by default · SEC-01.5.2 · MUST · DEV
Endpoints should be authenticated by default and anonymous access should have to be explicitly granted, for example to a ‘login’ endpoint.
APIs must enforce the same-origin policy · SEC-01.5.3 · MUST · DEV
APIs must enforce the same origin policy. If an API is hosted on a different domain/subdomain to a front-end application then this can be relaxed, but only to allow those specific origins as required.
Web applications must define a content security policy (CSP) · SEC-01.6.1 · MUST · DEV
A Content Security Policy header is used to define where assets such as scripts and stylesheets can be loaded from and whether inline execution of such as assets is allowed.
The Audacia.SecureHeadersMiddleware library contains a configurable Content Security Policy for .NET apps.
Web applications must include and exclude HTTP headers following security best practices · SEC-01.6.2 · MUST · DEV
Certain HTTP headers are recommended to be present in (or absent from) all responses for security reasons -see OWASP recommended HTTP headers.
The Audacia.SecureHeadersMiddleware library contains a configurable ASP.NET Core middleware to add the appropriate headers for .NET apps. It also has default configuration for APIs and MVC applications.
Web applications must include the relevant HTTP headers when there are SEO considerations · SEC-01.6.3 · MUST · DEV
Use the X-Robots-Tag meta HTTP tag, which tells web crawlers what they are allowed to do when they visit a site (and are more highly valued than a robots.txt file).
The appropriate OWASP guidance must be followed · SEC-01.7.1 · MUST · DEV/TEST
Teams must follow current OWASP guidance relevant to the system and document the risks considered and the controls applied.
- OWASP Top 10: Common web application risks (e.g. injection, broken access control, cryptographic failures, insecure design, vulnerable and outdated components, SSRF). See the OWASP Top 10.
- OWASP Top 10 for LLM Applications: Risks specific to LLM systems (e.g. prompt injection, data poisoning, supply chain/model integrity, sensitive information disclosure). See the OWASP Top 10 for LLM Applications.
- OWASP Top 10 for Business Logic Abuse: Abuse of workflows and rules (e.g. bypassing business processes, DoS, privilege escalation through logic flaws, quota/metering circumvention). See the OWASP Top 10 for Business Logic Abuse.
All externally accessible services must use a custom domain · CLOUD-02.1 · MUST · DEV
All deployed environments must expose services via custom domains. Default or auto-generated platform URLs (e.g. app-name.azurewebsites.net, xyz.cloudfront.net) must not be used for direct access in any environment.
This improves:
- URL readability and user trust
- Compatibility with enterprise SSO and OAuth
- Portability across hosting providers
Secure all custom domains using managed SSL/TLS certificates · CLOUD-02.2 · MUST · DEV
Custom domains must be protected with automatically managed SSL/TLS certificates offered by the hosting or CDN provider. These certificates should:
- Support automatic renewal
- Be provisioned through DNS or HTTP challenge
- Avoid manual upload workflows unless explicitly justified
This ensures continuous HTTPS support without administrative overhead or security risks.
EXCEPTION This does not apply where SSL/TLS certificates are managed by the client.
Follow a standardised domain naming convention · CLOUD-02.3 · MUST · DEV
All custom domains must follow a consistent structure to clearly identify:
- The service name
- The tenant/customer (if multi-tenant)
- Optional: The deployed environment
Recommended pattern
Use centralised DNS management · CLOUD-02.4 · SHOULD · DEV
DNS zones should be managed via a centralised, version-controlled configuration using Infrastructure as Code (e.g. Terraform, Pulumi, Bicep).
Benefits include:
- Reviewed, auditable changes
- Avoidance of duplicated or conflicting records
- Reproducibility across multiple environments
Automate domain provisioning and validation · CLOUD-02.5 · COULD · DEV
Where feasible, automate the provisioning of domains, validation of SSL certificates, and creation of DNS records as part of the deployment process.
This supports:
- Faster, safer deployments
- Reduced manual configuration errors
- Stronger alignment with GitOps and infrastructure-as-code workflows
Cloud resources must have descriptive and meaningful names · CLOUD-01.1 · MUST · DEV
Use names that accurately describe the resource’s purpose, function, or role. Avoid ambiguous or generic names that may lead to confusion or misinterpretation. Ensure names convey relevant information without being overly long or complex.
Example 1:
- Good:
olympus-dev-vm - Bad:
vm1
Example 2:
- Good:
olympus-dev-asp-functions - Bad:
app-service-plan
Example 3:
- Good:
olympusdevst - Bad:
storageaccount
Cloud resources must be named consistently · CLOUD-01.2 · MUST · DEV
Establish a consistent naming convention that is applied uniformly across all resources. Include all relevant teams and stakeholders in the development of the naming standard to ensure buy-in and alignment.
Good:
olympus-dev-asp-functionsolympus-qa-asp-functionsolympus-uat-asp-functions
Bad:
olympus-dev-service-plan-functionsolympus-qa-asp-functionsolympus-uat-app-service-plan-functions
Resource names must use lowercase letters and numbers · CLOUD-01.3 · MUST · DEV
Stick to lowercase letters and numbers to avoid potential case sensitivity issues and ensure consistency. Avoid using special characters, spaces, or uppercase letters in resource names.
Resource names must include an environment indicator · CLOUD-01.4 · MUST · DEV
Include environment indicators to differentiate resources across development, production, staging, or testing environments. Use abbreviations or standardized keywords such as “dev”, “prod”, “staging” or “qa”.
Generic: {project}-{environment}-*
Examples:
olympus-dev-*olympus-qa-*olympus-uat-*olympus-prod-*
Resource names must include purpose or resource type indicators · CLOUD-01.5 · MUST · DEV
Use the purpose and the type of the resource to categorize the resources based on their purpose, department, or function. Prefixes provide a quick way to identify resource types and aid in navigation and management.
Generic: {project}-{environment}-{purpose*}-{resource}-*
purpose*: A prefix indicating the purpose of the resource in the context of the system.
Examples:
olympus-qa-portal-apiolympus-qa-public-apiolympus-qa-orders-funcolympus-qa-timers-funcolympus-qa-customers-sqldbolympus-qa-sales-sqldbolympus-qa-language-vnetolympus-qa-build-vnet
Resource names must include a location indicator · CLOUD-01.6 · MUST · DEV
Include location indicators for resources deployed across multiple regions or geographical locations. Use standardized location abbreviations or keywords to maintain consistency.
Generic: {project}-{environment}-{resource}-{location}
olympus-qa-sqldb-uksoutholympus-qa-sqldb-westeuolympus-qa-cosmos-westusolympus-qa-cosmos-usva
Resource names must be concise and clear · CLOUD-01.7 · MUST · DEV
Keep resource names concise while maintaining clarity and conveying relevant information. Avoid excessively long or cryptic names that may hinder readability and management.
Project-specific conventions must be documented · CLOUD-01.8 · MUST · DEV
If the project requires a different naming convention this should be documented and distributed among relevant teams and stakeholders. Provide clear guidelines, examples, and explanations to ensure proper understanding and adoption of the naming standard. Conduct training or workshops to educate teams on the importance of adhering to the naming conventions.
Web applications should utilise a Web Application Firewall (WAF) to protect systems from common attacks · SEC-02.2 · SHOULD · DEV
This should always be presented as an option to clients, with benefits and costs clearly explained.
Cloud-hosted applications should be deployed within an isolated virtual network · SEC-02.3 · SHOULD · DEV
This approach introduces additional cost and complexity, so should be presented as an option to clients for them to understand the pros and cons.
Due diligence must be performed before introducing a new third-party dependency · SEC-04.1 · MUST · DEV/TEST
The due diligence must, at a minimum, cover the following questions (with an aim to answer “Yes” to every question):
- Is the dependency stable, popular, etc. (using metrics like GitHub stars and downloads)?
- Is the dependency actively maintained (using metrics like number of contributors and last update)?
- Is the latest version secure (i.e. no unpatched vulnerabilities)?
- Is the dependency solving a well-scoped, common problem (e.g. Excel integration or date parsing)?
- Does the cost to build outweigh the risk of importing?
- Is the dependency solving a non-trivial problem or one that likely to evolve in future (i.e. it would be harder to write our own code)?
- Is the license acceptable for our intended use (e.g. copyleft licenses are generally to be avoided)?
Performance & Scalability
Regular database reindexing must be performed · PERF-01.1 · MUST · DEV
Where possible, regular database reindexing should be automated, e.g. a script running on a schedule.
Data archiving must be considered · PERF-01.2 · MUST · DEV
This may not always be possible, but must always be considered.
Database queries must be optimized · PERF-01.3 · MUST · DEV
Basic optimizations must be performed, for example:
- Projecting entities to a DTO if an untracked subset of data is needed
- Applying in-database filtering to restrict data returned to what is required
- Using the most efficient aggregation technique, e.g. if using Entity Framework, prefer
Any()overCount() > 0 - If using Entity Framework, eliminate unnecessary
Include()statements and don’t track entities that will not be updated
Systems should utilise caching in order to provide more scalable data access · PERF-02.2 · SHOULD · DEV
Caching increases cost and complexity, therefore this must be a consideration rather than a mandate, and the costs and benefits should be communicated to the client where appropriate.
A distributed caching solution should generally be preferred in order to best enable horizontal scalability.
Asynchronous operations should be preferred where possible · PERF-02.3 · SHOULD · DEV
Where an operation does not need to be performed synchronously, technologies like message queues and events should be used in order to process actions asynchronously.
Common examples include:
- Sending an email
- Writing to an audit log
- Notifying an external system of an event