Code Quality Standards
This document defines the comprehensive code quality standards for the Fawkes Internal Product Delivery Platform. All code contributions must adhere to these standards to maintain consistency, security, and maintainability.
Overview
Fawkes enforces code quality through:
- Automated Linting - Pre-commit hooks and CI/CD checks
- Security Scanning - Secrets detection and vulnerability scanning
- Style Enforcement - Language-specific formatters and linters
- Quality Gates - CI/CD pipeline gates that block low-quality code
Quick Start
# Install pre-commit hooks
make pre-commit-setup
# Run all linters
make lint
# Run linters on specific files
pre-commit run --files path/to/file
💡 TIP: Set up format-on-save in your IDE for automatic formatting. See the Format-on-Save Setup Guide for detailed instructions.
Language-Specific Standards
Bash
Formatter: shfmt Linter: ShellCheck
Standards:
- Use
#!/usr/bin/env bashshebang - Indentation: 2 spaces
- Case indentation: Indent case statements
- Binary next line: Place
&&and||at beginning of line - Simplify redirects: Use simplified redirect syntax
- Use double quotes for variables:
"${var}" - Check exit codes with
|| exit 1 - Use
[[ ]]instead of[ ]for conditionals - Add
set -euo pipefailat script start for safety
Configuration:
- shfmt args in
.pre-commit-config.yaml:-i 2 -ci -bn -sr - ShellCheck:
.shellcheckrc(configured for warning-level severity, bash dialect)
Example:
#!/usr/bin/env bash
set -euo pipefail
# Good
if [[ "${var}" == "value" ]]; then
echo "Variable is set"
fi
# Bad
if [ $var == "value" ]; then # Missing quotes, using [ ]
echo "Variable is set"
fi
Pre-commit hook:
- repo: https://github.com/shellcheck-py/shellcheck-py
hooks:
- id: shellcheck
args: ["--severity=warning"]
- repo: https://github.com/scop/pre-commit-shfmt
hooks:
- id: shfmt
args: ["-i", "2", "-ci", "-bn", "-sr", "-w"]
Python
Linters: Black (formatter), Flake8 (linter)
Standards:
- Line length: 120 characters (extended from default 88)
- Import order: stdlib → third-party → local
- Type hints: Required for all public functions
- Docstrings: Google style for all public functions/classes
- Naming:
snake_casefor functions and variablesPascalCasefor classesUPPER_CASEfor constants
Configuration:
# .pre-commit-config.yaml
- repo: https://github.com/psf/black
hooks:
- id: black
args: []
- repo: https://github.com/pycqa/flake8
hooks:
- id: flake8
args: ['--max-line-length=120', '--extend-ignore=E203,W503']
Example:
"""Module for user management."""
from typing import Optional
def create_user(username: str, email: str, active: bool = True) -> dict:
"""Create a new user.
Args:
username: The username for the new user
email: User's email address
active: Whether the user is active (default: True)
Returns:
Dictionary containing user details
Raises:
ValueError: If username or email is invalid
"""
if not username or not email:
raise ValueError("Username and email are required")
return {
"username": username,
"email": email,
"active": active,
}
IDE Integration:
# VS Code settings.json
{
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": ["--max-line-length=120"],
"python.formatting.provider": "black",
"editor.formatOnSave": true
}
Go
Linter: golangci-lint (recommended)
Standards:
- Go version: 1.21+
- Package naming: Short, lowercase, no underscores
- Error handling: Always check errors, don't ignore
- Context: Pass context.Context as first parameter
- Comments: Exported functions/types must have comments
Configuration: .golangci.yml
linters:
enable:
- gofmt
- golint
- govet
- errcheck
- staticcheck
- gosimple
- ineffassign
- unused
linters-settings:
gofmt:
simplify: true
Example:
// Package user provides user management functionality.
package user
import (
"context"
"fmt"
)
// User represents a user in the system.
type User struct {
ID string
Username string
Email string
}
// CreateUser creates a new user in the database.
func CreateUser(ctx context.Context, username, email string) (*User, error) {
if username == "" || email == "" {
return nil, fmt.Errorf("username and email are required")
}
user := &User{
ID: generateID(),
Username: username,
Email: email,
}
return user, nil
}
Pre-commit hook (add to .pre-commit-config.yaml):
- repo: https://github.com/golangci/golangci-lint
rev: v1.55.0
hooks:
- id: golangci-lint
YAML
Formatter: Prettier Linter: yamllint
Standards:
- Indentation: 2 spaces
- Line length: 120 characters (warning only)
- Document start: Not required (
---optional) - Trailing spaces: Not allowed
- Empty lines: Maximum 2 consecutive
Configuration:
- Formatting:
.prettierrc - Linting:
.yamllint
extends: default
rules:
line-length:
max: 120
level: warning
document-start: disable
comments-indentation: disable
indentation:
spaces: 2
indent-sequences: consistent
Example:
# Good
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
app: my-app
version: v1.0.0
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
JSON
Formatter: Prettier Linter: check-json (built-in to pre-commit-hooks)
Standards:
- Indentation: 2 spaces
- Trailing commas: Not allowed (JSON spec)
- Comments: Not supported (use YAML if comments needed)
- Keys: Use camelCase or snake_case consistently
Configuration: .prettierrc
Pre-commit hook:
- repo: https://github.com/pre-commit/pre-commit-hooks
hooks:
- id: check-json
- repo: https://github.com/pre-commit/mirrors-prettier
hooks:
- id: prettier
types_or: [json]
Example:
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "app-config",
"namespace": "default"
},
"data": {
"logLevel": "info",
"maxConnections": "100"
}
}
Markdown
Formatter: Prettier Linter: markdownlint-cli
Standards:
- Line length: Disabled (some docs need long lines)
- Multiple H1s: Allowed (for documentation)
- Trailing spaces: Not allowed
- Lists: Consistent markers (- or *)
- Code blocks: Language specifier required
Configuration:
- Formatting:
.prettierrc - Linting:
.markdownlint.json
{
"MD013": false, // Disable line length
"MD041": false, // Disable first line heading
"MD025": false, // Allow multiple H1s
"MD047": false, // Disable single trailing newline
"MD032": false, // Disable blank lines around lists
"MD034": false, // Disable bare URLs
"MD040": false // Disable fenced code language
}
Example:
# Component Architecture
## Overview
The component follows the microservices pattern with the following structure:
- API Gateway
- Service Layer
- Data Layer
### Code Example
```python
def hello_world():
return "Hello, World!"
```
### Terraform
**Linters**: terraform fmt, terraform validate, TFLint, tfsec
**Standards**:
- **Terraform version**: 1.6+ syntax
- **Provider versions**: Always specify in `required_providers`
- **Variables**: All configurable values as variables
- **Outputs**: Important values exported
- **Naming**: snake_case for all resources
- **Comments**: Complex logic documented
- **Security**: tfsec security checks passing
**Configuration**: `.tflint.hcl`
```hcl
plugin "aws" {
enabled = true
version = "0.29.0"
}
plugin "azurerm" {
enabled = true
version = "0.25.1"
}
rule "terraform_naming_convention" {
enabled = true
format = "snake_case"
}
Example:
# Terraform >= 1.6
terraform {
required_version = ">= 1.6.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
# Variable definition with description
variable "resource_group_name" {
description = "Name of the Azure resource group"
type = string
}
variable "location" {
description = "Azure region for resources"
type = string
default = "eastus"
}
# Resource with proper naming
resource "azurerm_resource_group" "main" {
name = var.resource_group_name
location = var.location
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
# Output important values
output "resource_group_id" {
description = "ID of the created resource group"
value = azurerm_resource_group.main.id
}
Pre-commit hooks:
- repo: https://github.com/antonbabenko/pre-commit-terraform
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_tflint
- id: terraform_docs
- id: terraform_tfsec
Security Standards
Secrets Detection
Tools: Gitleaks, detect-secrets
Standards:
- Zero tolerance for committed secrets
- Pre-commit: Blocks commits with secrets
- CI/CD: Pipeline fails on secrets detection
- Environment variables: Use for all secrets
- Vault integration: For production secrets
Configuration: .gitleaks.toml
[allowlist]
description = "Allowlist for false positives"
# Test fixtures
paths = [
"tests/fixtures/",
"examples/",
]
Example (BAD):
# ❌ NEVER do this
api_key = "sk-1234567890abcdef" # pragma: allowlist secret
database_url = "postgresql://user:password@localhost/db" # pragma: allowlist secret
Example (GOOD):
# ✅ Use environment variables
import os
api_key = os.environ.get("API_KEY")
database_url = os.environ.get("DATABASE_URL")
if not api_key or not database_url:
raise ValueError("Required environment variables not set")
Container Security
Tool: Trivy
Standards:
- Severity threshold: CRITICAL and HIGH fail pipeline
- Base images: Use official, updated images
- Updates: Regular security patching
- Overrides: Document in
.trivyignorewith expiration
Example Dockerfile:
# Use specific version tag, not latest
FROM python:3.11-slim-bookworm
# Update packages for security
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y --no-install-recommends \
ca-certificates && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Non-root user
RUN useradd -m -u 1000 appuser
USER appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
CMD ["python", "app.py"]
Code Quality (SAST)
Tool: SonarQube
Standards:
- Code coverage: Minimum 80% for new code
- Bugs: Zero new bugs
- Vulnerabilities: Zero new vulnerabilities
- Security hotspots: 100% reviewed
- Code smells: Address critical/major issues
IDE Integration
VS Code
Extensions:
{
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-azuretools.vscode-docker",
"redhat.vscode-yaml",
"hashicorp.terraform",
"golang.go",
"timonwong.shellcheck",
"davidanson.vscode-markdownlint"
]
}
Settings (.vscode/settings.json):
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.formatting.provider": "black",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"[yaml]": {
"editor.defaultFormatter": "redhat.vscode-yaml"
},
"[terraform]": {
"editor.defaultFormatter": "hashicorp.terraform"
}
}
IntelliJ IDEA / PyCharm
Plugins:
- Python Plugin (PyCharm has built-in)
- Terraform and HCL
- Kubernetes
- Bash Support
- Markdown Support
Settings:
- File → Settings → Tools → File Watchers → Add Black
- File → Settings → Editor → Code Style → Python → Set line length to 120
- File → Settings → Editor → Inspections → Enable all Python inspections
Vim/Neovim
Plugins (via vim-plug):
Plug 'dense-analysis/ale'
Plug 'hashivim/vim-terraform'
Plug 'fatih/vim-go'
Plug 'ambv/black'
Configuration:
" ALE linters
let g:ale_linters = {
\ 'python': ['flake8', 'black'],
\ 'bash': ['shellcheck'],
\ 'terraform': ['tflint'],
\ 'go': ['golangci-lint'],
\}
" Auto-format on save
let g:ale_fixers = {
\ 'python': ['black'],
\ 'terraform': ['terraform'],
\}
let g:ale_fix_on_save = 1
CI/CD Integration
Pre-commit CI Workflow
Location: .github/workflows/pre-commit.yml
Features:
- Runs on all pull requests
- Runs on push to main/develop
- Caches dependencies for speed
- Comments on PR with failures
- Uploads artifacts for review
Workflow:
name: Pre-commit Validation
on:
pull_request:
branches: [main, develop]
push:
branches: [main, develop]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Install pre-commit
run: pip install pre-commit
- name: Run pre-commit
run: pre-commit run --all-files
Quality Gates
Quality gates enforce standards at multiple checkpoints:
Code Commit → Pre-commit Hooks → PR → CI Linting → Security Scan → Build → Deploy
↓ ↓ ↓ ↓ ↓ ↓ ↓
Local Check Local Check GitHub GitHub Actions SonarQube Harbor ArgoCD
Gate Levels:
- Pre-commit (Local): Immediate feedback on commit
- PR Check (GitHub): Blocks merge on failure
- CI Linting (GitHub Actions): Comprehensive checks
- Security Scan (SonarQube/Trivy): Vulnerability detection
- Quality Gate (SonarQube): Code quality metrics
Common Issues and Solutions
Pre-commit Hook Failures
Issue: Hook fails with "command not found"
Solution: Install the required tool
# Terraform tools
brew install terraform tflint terraform-docs tfsec
# Kubernetes tools
brew install kubectl kubeval kustomize helm
# Python tools
pip install black flake8 pre-commit
# Shell tools
brew install shellcheck
Issue: Hook times out or is very slow
Solution: Use specific hooks instead of all
# Run specific hook
pre-commit run terraform_fmt --all-files
# Skip slow hooks temporarily
SKIP=terraform_tflint,mkdocs-validate pre-commit run --all-files
Linting Failures
Issue: Black and Flake8 conflict on line length
Solution: Both are configured for 120 chars, but ensure consistency:
# .flake8
[flake8]
max-line-length = 120
extend-ignore = E203, W503
Issue: Terraform fmt changes files but validation fails
Solution: Run in order:
terraform fmt -recursive
terraform validate
Issue: ShellCheck reports issues in vendor scripts
Solution: Exclude vendor directories:
# .pre-commit-config.yaml
- id: shellcheck
exclude: '^vendor/|^third-party/'
Security Scanning Issues
Issue: Secrets detected in test fixtures
Solution: Add to allowlist:
# .gitleaks.toml
[allowlist]
paths = [
"tests/fixtures/",
"examples/test-data.json",
]
Issue: Trivy reports false positive CVE
Solution: Document and ignore:
# .trivyignore
# PostgreSQL CVE - not applicable, using prepared statements
CVE-2023-12345 exp:2024-12-31
Best Practices
DO ✅
- Run pre-commit before pushing:
pre-commit run --all-files - Install all linting tools locally: Get immediate feedback
- Configure IDE integration: Auto-format on save
- Address linting issues immediately: Don't accumulate technical debt
- Document overrides: Explain why rules are disabled
- Update tools regularly:
pre-commit autoupdate - Review linting in code reviews: Quality is everyone's responsibility
DON'T ❌
- Don't skip hooks:
--no-verifyshould be rare emergency only - Don't commit commented-out code: Remove it (Git history preserves it)
- Don't disable rules globally: Use specific exclusions
- Don't ignore security warnings: Address or document why safe
- Don't commit secrets: Even temporarily, even to feature branches
- Don't mix formatting changes: Separate formatting from logic changes
Getting Help
Resources
- Format-on-Save Setup: docs/how-to/development/format-on-save-setup.md
- Pre-commit Documentation: docs/PRE-COMMIT.md
- Security Quality Gates: docs/how-to/security/quality-gates-configuration.md
- Contributing Guide: docs/contributing.md
Support Channels
- GitHub Issues: Report issues
- GitHub Discussions: Ask questions
- Mattermost: #platform-help channel
Quick Commands Reference
# Setup
make pre-commit-setup # Install pre-commit hooks
make setup-vscode # Configure VS Code
# Formatting
make format # Apply all code formatters (Black, shfmt, prettier, terraform fmt)
make format-check # Check formatting without changes (fails if files need formatting)
# Linting
make lint # Run all linters
pre-commit run --all-files # Run all pre-commit hooks
pre-commit run terraform_fmt # Run specific hook
# Validation
make validate # Validate manifests
make terraform-validate # Validate Terraform
make k8s-validate # Validate Kubernetes
# Updates
pre-commit autoupdate # Update hook versions
pre-commit clean # Clean hook cache
Continuous Improvement
Code quality standards evolve. We review and update:
- Monthly: Tool versions and configurations
- Quarterly: Standards based on team feedback
- Per-release: Major updates to linting tools
Feedback: Open an issue or PR to suggest improvements to these standards.
Maintained by: Fawkes Platform Team Last Updated: December 2024 Related Issues: #109 (Code Quality Standards)