Skip to main content

Overview

GOV.UK Notify API is the backend service powering GOV.UK Notify. Contributions should maintain high code quality, security standards, and alignment with government service design principles.

Before You Start

Required Reading

Access Requirements

  • GitHub account with access to alphagov/notifications-api
  • AWS credentials for local development (see Wiki)
  • Access to the credentials repository for API keys

Code Standards

Python Version

All code must be compatible with Python 3.13.

Code Style

We use Ruff for linting and formatting:
# Check for issues
ruff check .

# Auto-fix issues
ruff check --fix .

# Format code
ruff format .

Type Hints

Use type hints for all function signatures. We use mypy for static type checking:
from typing import Optional

def send_notification(template_id: str, recipient: str) -> dict[str, str]:
    # Function implementation
    pass
Run type checking:
mypy

Pre-commit Hooks

All commits must pass pre-commit hooks:
# Install hooks (one time)
pre-commit install --install-hooks

# Manually run on all files
pre-commit run --all-files
Hooks automatically:
  • Remove trailing whitespace
  • Fix end-of-file issues
  • Validate YAML files
  • Detect debug statements
  • Run Ruff linting and formatting

Development Workflow

1. Create a Feature Branch

git checkout -b feature/your-feature-name
Branch naming conventions:
  • feature/ - New features
  • fix/ - Bug fixes
  • refactor/ - Code refactoring
  • docs/ - Documentation updates

2. Make Your Changes

Follow the project structure:
app/
├── v2/                      # Public API endpoints
│   ├── notifications/       # Feature grouping
│   │   ├── __init__.py     # Blueprint registration
│   │   └── post_notifications.py  # Individual endpoint
│   └── errors.py            # Public API error handling
├── <feature>/               # Internal features
│   ├── rest.py             # Internal REST endpoints
│   └── ...
├── dao/                     # Data access objects
├── models.py                # SQLAlchemy models
└── schemas.py               # Marshmallow schemas

3. Write Tests

All changes require tests:
# Run tests while developing
make watch-tests

# Run full test suite
make test
Test coverage expectations:
  • New features: Comprehensive unit tests
  • Bug fixes: Regression tests
  • Refactoring: Maintain existing coverage

4. Update Documentation

If your changes affect:
  • Public APIs: Update client documentation and API docs
  • Configuration: Update setup documentation
  • Developer workflows: Update this guide

5. Run the Full Test Suite

Before creating a pull request:
make test
Ensure:
  • All tests pass
  • Linting passes
  • Type checking passes
  • No new warnings

6. Commit Your Changes

Write clear, descriptive commit messages:
git add .
git commit -m "Add SMS delivery status webhook endpoint

- Create new v2 endpoint for delivery status callbacks
- Add validation for provider-specific payload formats
- Include tests for MMG and Firetext providers

Closes #123"
Commit message format:
  • First line: Brief summary (50 chars or less)
  • Blank line
  • Detailed description of changes
  • Reference related issues

7. Push and Create Pull Request

git push origin feature/your-feature-name
Create a pull request on GitHub with:
  • Clear title describing the change
  • Description of what changed and why
  • Link to related issues
  • Screenshots (if UI changes)
  • Testing instructions

Public API Guidelines

When creating or modifying public API endpoints:

File Structure

Each endpoint should be in its own file:
app/v2/inbound_sms/
├── __init__.py              # Blueprint registration
└── get_inbound_sms.py       # Endpoint implementation

Blueprint Registration

Create blueprints in __init__.py:
from flask import Blueprint
from app.v2.errors import register_errors

v2_notification_blueprint = Blueprint(
    "v2_notifications",
    __name__,
    url_prefix='/v2/notifications'
)

register_errors(v2_notification_blueprint)

Error Handling

Public APIs use different error handling than internal endpoints:
# Public API errors (app/v2/errors.py)
register_errors(v2_notification_blueprint)

# Internal API errors (app/errors.py)
# Different format and detail level

Authentication

Specify authentication in app/__init__.py:
v2_notification_blueprint.before_request(requires_auth)
application.register_blueprint(v2_notification_blueprint)

Client Support

All public API endpoints must:
  1. Have adapters in all client libraries (Python, Ruby, Java, .NET, Node.js, PHP)
  2. Be documented in client documentation
  3. Be documented in the technical documentation

Database Changes

Migrations

Create database migrations using Flask-Migrate:
# Create a new migration
flask db migrate -m "Add index to notifications table"

# Review the generated migration in migrations/versions/

# Apply migrations
flask db upgrade

Migration Guidelines

  • Always review auto-generated migrations
  • Test migrations both up and down
  • Consider impact on production (data volume, locking)
  • Use op.batch_alter_table() for SQLite compatibility in tests

SQL Review

For complex migrations, use squawk-cli to check for issues:
# Included in test dependencies
squawk-cli migrations/versions/xxx_migration.py

Dependency Management

Adding Dependencies

Add dependencies to .in files:
# For production dependencies
echo "new-package==1.2.3" >> requirements.in

# For test dependencies
echo "new-test-package==1.2.3" >> requirements_for_test.in

# Compile and sync
make freeze-requirements

Updating Dependencies

See the Dependencies Wiki for detailed guidance. Update notifications-utils:
make bump-utils

Security

Security Considerations

  • Never commit secrets or API keys
  • Use environment variables for sensitive data
  • Validate all user inputs
  • Sanitize data before database queries
  • Follow OWASP guidelines for web applications

Secrets Management

  • Store secrets in AWS Secrets Manager
  • Use environment.sh for local development (git-ignored)
  • Never hardcode credentials

Testing Requirements

Unit Tests

All code changes require tests:
import pytest
from app.models import Notification

def test_create_notification(sample_template):
    notification = Notification(
        template=sample_template,
        to="[email protected]"
    )
    assert notification.status == "created"

Integration Tests

Test interactions between components:
def test_send_email_via_api(client, sample_api_key):
    response = client.post(
        "/v2/notifications/email",
        json={"email_address": "[email protected]"},
        headers={"Authorization": f"Bearer {sample_api_key}"}
    )
    assert response.status_code == 201

Mocking External Services

Always mock external services:
from moto import mock_s3
from freezegun import freeze_time

@mock_s3
@freeze_time("2024-01-15")
def test_archive_old_notifications():
    # Your test code
    pass

Code Review

All pull requests require code review:

Reviewers Should Check

  • Code follows style guidelines
  • Tests are comprehensive
  • Documentation is updated
  • No security vulnerabilities
  • Performance implications considered
  • Backward compatibility maintained

Submitters Should

  • Respond to feedback promptly
  • Make requested changes
  • Explain reasoning for decisions
  • Keep pull requests focused and small

Deployment

Pre-deployment Checks

  • All tests pass in CI/CD
  • Database migrations reviewed
  • Feature flags configured (if applicable)
  • Rollback plan documented

Migration Safety

Check for pending migrations:
make check-if-migrations-to-run

Getting Help

Resources

Common Issues

See Troubleshooting in the setup guide.

Release Process

The team follows a continuous deployment model:
  1. Changes merge to main
  2. Automated tests run in Concourse
  3. Successful builds deploy to staging
  4. Manual promotion to production

License

By contributing, you agree that your contributions will be licensed under the MIT License.

Code of Conduct

Follow the GDS Code of Conduct and government digital service standards.

Build docs developers (and LLMs) love