Skip to main content

Defra Standards Skill — Example

This is an example Agent Skill that packages Defra software development standards so agents can discover and load them automatically. Copy the defra-standards/ folder into .github/skills/ in your repository.

Unlike an instruction file (which is always active), this skill activates only when Copilot is working on a Defra service codebase — reducing noise for non-Defra projects that use the same agent configuration.

Directory structure

.github/skills/
└── defra-standards/
    └── SKILL.md

Example file contents

The content below goes into your .github/skills/defra-standards/SKILL.md file:


---
name: defra-standards
description: Defra software development standards for Node.js and .NET government services. Use when writing, reviewing, or refactoring code in a Defra digital service repository. Activates for Hapi framework projects, CDP platform services, and .NET ASP.NET Core Minimal API services.
license: OGL-UK-3.0
metadata:
  author: defra-digital
  version: "1.0"
---

# Defra Standards

Apply these standards when generating or reviewing code in a Defra digital service.

## Runtime and language

### Node.js
- Use Node.js Active LTS version only
- Use vanilla JavaScript — do not use TypeScript without an approved exception
- Use ES modules (`import`/`export`) by default; CommonJS (`require`) only where required (e.g. Jest config)
- Use `const` by default, `let` only when reassignment is required, never `var`
- Use `async/await` — do not use callbacks or raw `.then()` chains

### .NET
- Use .NET Active LTS version only
- Use C# latest stable with nullable reference types enabled
- Use `record` types for immutable data
- .NET is for backend services and APIs only — do not use Razor Pages, Blazor, MVC views, or any other .NET UI framework. Frontends must be built with Node.js + Hapi + GOV.UK Frontend (Nunjucks). See [Defra .NET Standards](https://defra.github.io/software-development-standards/standards/net_standards/).

## Approved and discouraged packages

### Node.js — approved

| Package | Purpose |
|---------|---------|
| `@hapi/hapi` | HTTP server |
| `@hapi/boom` | HTTP error responses |
| `@hapi/crumb` | CSRF protection |
| `@hapi/blankie` | Content Security Policy |
| `@hapi/inert` | Static file serving |
| `@hapi/vision` | Template rendering (Nunjucks) |
| `@hapi/yar` | Session management |
| `joi` | Schema validation (standalone) |
| `nunjucks` | Server-side templating |
| `winston` | Structured JSON logging |
| `convict` | Configuration with validation |

### Node.js — discouraged

- `express`, `fastify`, `koa` — use Hapi
- `@hapi/joi` — deprecated, use standalone `joi`
- `mongoose` — use the native MongoDB driver
- `moment` — use native `Intl.DateTimeFormat` or `date-fns`
- `lodash` — use native array/object methods
- `request` or `axios` — use native `fetch` or `undici`
- `jquery` — not needed with progressive enhancement
- `typescript` — requires approved exception

## CDP platform conventions

If the service runs on the Defra Core Delivery Platform (CDP):

- Use `convict` with `convict-format-with-validator` for all configuration — never access `process.env` directly in application code
- Use `~` as an absolute import alias for project files
- Use `request.logger` for logging inside route handlers — do not use `console.log`
- Use `@defra/hapi-secure-context` for TLS certificate management
- Use `@defra/cdp-metrics` for Prometheus metrics
- Prefix all custom CSS classes with `app-` using BEM — e.g. `.app-heading`
- Add `data-testid` attributes to all key UI elements

## Containerisation

- Use Docker with Defra base images:
  - `defradigital/node` (production)
  - `defradigital/node-development` (development/CI)
  - `defradigital/dotnetcore` (production .NET)
  - `defradigital/dotnetcore-development` (development/CI .NET)
- Do not use the generic `node:lts` or `mcr.microsoft.com/dotnet` images directly

## Architecture

Keep controllers thin: validate input → call a service → render a view. Business logic belongs in service modules, not route handlers.

```
Route handler → validates input (joi) → calls service → renders view or returns response
```

## Naming conventions

- **Directories and template files**: `kebab-case` (e.g. `my-component.njk`)
- **JavaScript files**: `camelCase` (e.g. `applicationService.js`)
- **Routes (URL paths)**: lowercase with hyphens (e.g. `/submit-application`)
- **Environment variables**: `UPPER_SNAKE_CASE` (e.g. `DATABASE_HOST`)
- **C# classes**: `PascalCase`
- **C# private fields**: `_camelCase`

## Version control

- Main branch is always shippable
- Branch naming: `<type>/<brief-description>` (types: `feature/`, `fix/`, `docs/`, `refactor/`, `test/`, `chore/`)
- Commit messages: conventional format (`feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`)
- Squash and merge PRs

## Quality gates

- Linter passes (`npx neostandard`) — do not extend or modify the ruleset
- All tests pass (`npm test`)
- Unit test coverage ≥90% — must not decrease from project or SonarCloud baseline
- SonarCloud quality gate passes (SonarWay profile) — no new bugs, vulnerabilities, or code smells

Back to skills index · Back to Getting Started