Skip to content

Codeing gudelines

1. General Principles

  • Readability First — Code is read far more often than it is written. Prioritize clarity over cleverness.
  • Keep It Simple (KISS) — Avoid unnecessary complexity. Simple solutions are easier to understand, test, and maintain.
  • Don't Repeat Yourself (DRY) — Eliminate duplication. Extract repeated logic into functions, classes, or modules.
  • Single Responsibility Principle (SRP) — Every module, class, or function should have one clear responsibility.
  • You Aren't Gonna Need It (YAGNI) — Don't add features or abstractions "just in case."
  • Boy Scout Rule — Leave the code better than you found it (small improvements during changes).
  • Continuous Improvement — Code health should improve over time, not degrade. Favor changes that enhance maintainability even if imperfect.

Follow the SOLID principles for object-oriented code:

  • Single Responsibility
  • Open/Closed
  • Liskov Substitution
  • Interface Segregation
  • Dependency Inversion

2. Naming Conventions

  • Use descriptive, meaningful names that reveal intent (e.g., calculateUserTotalRevenue instead of calc).
  • Variables/functions: camelCase or snake_case (consistent with your language/project style).
  • Classes/Types: PascalCase.
  • Constants: UPPER_SNAKE_CASE.
  • Avoid abbreviations, single-letter names (except in loops like i, j for indices), or misleading names.
  • Be consistent across the codebase. Use the project's linter/formatter to enforce this.

Bad: x, tmp, data1 Good: userId, totalOrderAmount, fetchActiveUsers

3. Code Structure and Formatting

  • Follow a consistent style guide (e.g., PEP 8, Google Style, Airbnb for JS/TS, or your team's .editorconfig).
  • Use 2 or 4 spaces for indentation (never tabs). Configure your editor to enforce this.
  • Line length: Max 100–120 characters (configurable).
  • One statement per line.
  • Proper spacing around operators, commas, and brackets.
  • Braces: Consistent placement (e.g., opening brace on same line or new line per style guide).
  • Remove dead code, commented-out code, and unused imports/variables.
  • Group related code logically; separate unrelated concepts vertically.
  • Keep files focused and reasonably sized (break large files into modules).

Automate formatting with tools like Prettier, Black, ESLint, clang-format, or gofmt.

4. Functions and Methods

  • Keep functions small (ideally < 20–30 lines; one screen).
  • Do one thing only (high cohesion).
  • Use clear, descriptive names (verbs for functions: getUser, validateInput).
  • Limit parameters (ideally ≤ 4; use objects/configs for more).
  • Prefer pure functions where possible (no side effects).
  • Return early for error/edge cases instead of deep nesting.
  • Avoid deep nesting (max 2–3 levels for conditionals/loops).

Bad: Long functions with multiple responsibilities and deep if-else chains. Good: Short, focused functions that are easy to test and reuse.

5. Comments and Documentation

  • Write self-documenting code — good names and structure reduce the need for comments.
  • Comment why something is done, not what (the code should show "what").
  • Use comments for complex logic, trade-offs, or non-obvious decisions.
  • Keep comments up-to-date (outdated comments are worse than none).
  • Document public APIs, classes, and key modules (use JSDoc, docstrings, or similar).
  • Include file/module headers where helpful (purpose, author, high-level overview).

Bad: // increment counter Good: // Workaround for legacy API rate limit — remove after migration (ticket #123)

6. Error Handling and Defensive Programming

  • Handle errors explicitly (never ignore or swallow silently).
  • Use exceptions, error codes, or result types consistently.
  • Validate inputs early.
  • Check for edge cases: null/undefined, empty collections, zero, negative values, concurrency issues.
  • Use logging for unexpected states (without exposing sensitive data).
  • Ensure resources are properly released (e.g., close files, connections — use try/finally, using, RAII, or defer).

7. Testing and Reliability

  • Write unit, integration, and end-to-end tests for new/changed code.
  • Aim for high test coverage on critical paths.
  • Tests should be fast, independent, and readable.
  • Follow Arrange-Act-Assert pattern.
  • Test edge cases and error paths.
  • Refactor code to make it testable (e.g., dependency injection).

8. Performance and Efficiency

  • Write for clarity first; optimize only when proven necessary (profile first).
  • Avoid premature optimization.
  • Be mindful of time/space complexity (e.g., nested loops in hot paths).
  • Use efficient data structures and algorithms.
  • Minimize unnecessary allocations, I/O, or network calls.
  • Consider scalability (e.g., database queries, caching).

9. Security and Privacy

  • Validate and sanitize all inputs.
  • Avoid hard-coded secrets, credentials, or sensitive data.
  • Follow secure coding practices (e.g., OWASP guidelines).
  • Handle authentication, authorization, and data protection appropriately.
  • Prevent common vulnerabilities (SQL injection, XSS, CSRF, buffer overflows, etc.).
  • Log security events without leaking sensitive information.

10. Maintainability and Architecture

  • Minimize global variables and tight coupling.
  • Favor loose coupling and high cohesion.
  • Use meaningful abstractions (avoid over-engineering).
  • Make code modular and reusable where appropriate.
  • Ensure portability and avoid platform-specific assumptions unless necessary.
  • Remove magic numbers/strings — use named constants.

11. Code Review Process

All changes must go through code review. Reviewers should check against this document and ask:

  • Does it fulfill the requirements and work correctly?
  • Is it readable, maintainable, and well-structured?
  • Are there tests? Do they cover key cases?
  • Any security, performance, or reliability issues?
  • Does it improve overall code health?
  • Small, focused PRs are preferred (easier to review).

Reviewers: Be constructive, kind, and specific. Favor approval if the change improves the codebase overall (even if not perfect).

Authors: Keep PRs small, add context in descriptions, and address feedback promptly.

12. Tools and Automation

  • Linters & Formatters: ESLint/Prettier (JS/TS), pylint/Black (Python), Checkstyle/Spotless (Java), etc.
  • Static Analysis: SonarQube, CodeQL, or built-in IDE tools.
  • CI/CD: Enforce style, tests, and quality gates on every PR.
  • Code Coverage: Minimum threshold for critical code.
  • EditorConfig + IDE settings for consistency.

13. DevOps, Infrastructure as Code (IaC), Scripts & Pipeline Code

Treat all automation as code: version-control it, review it, test it, and apply the same quality standards as application code. Prefer declarative/idempotent approaches over imperative scripts where possible.

13.1 General DevOps Principles

  • Idempotency — Running the same code multiple times produces the same result without side effects.
  • Immutability where feasible (e.g., infrastructure provisioning).
  • Separation of Concerns — Use Terraform for provisioning, Ansible for configuration, pipelines for orchestration.
  • Small, focused changes — Keep PRs for IaC small and reviewable.
  • DRY & Modular — Extract reusable modules, roles, templates, or pipeline includes.
  • No secrets in code — Use secrets managers, variables, or external stores.
  • Environment parity — Use the same code for dev/staging/prod (parameterized via variables/TF vars/inventories).

13.2 Bash / Shell Scripts

  • Always start with #!/usr/bin/env bash (or #!/bin/bash).

  • Use strict mode at the top:

    Bash

    set -euo pipefail
    IFS=$'\n\t'
  • Keep scripts short (< 100–200 lines preferred; break large logic into functions or separate scripts).

  • Use descriptive function and variable names (e.g., install_dependencies, backup_database).

  • Quote all variables and command substitutions ("$var", "$(command)").

  • Handle errors explicitly; redirect errors to stderr.

  • Prefer built-in commands and avoid unnecessary external dependencies.

  • Add usage/help function and argument parsing (getopts or similar).

  • Make scripts idempotent and safe to re-run.

  • Document complex logic and non-obvious decisions.

  • Test with shellcheck (linting) and bats (unit testing) where applicable.

Bad: Unquoted variables, missing set -e, long monolithic scripts. Good: Strict mode, modular functions, clear error handling.

13.3 Ansible (Playbooks, Roles, YAML)

  • Organize in standard role structure (tasks/, handlers/, templates/, vars/, defaults/, meta/, files/, etc.).
  • Keep playbooks simple — mostly include roles; put logic in roles or custom modules.
  • Use descriptive names for tasks, variables, roles, and plays.
  • Prefer variables with defaults; use vars_files, group_vars, host_vars.
  • Make everything idempotent (use state: present, changed_when, creates, etc.).
  • Break complex tasks into multiple files or use blocks for error handling/rollback.
  • Use tags for selective execution.
  • Avoid shell/command modules when a dedicated module exists (e.g., apt, template, copy).
  • Validate with ansible-playbook --syntax-check and ansible-lint.
  • Write reusable roles; document with README.md and molecule tests.
  • Use Jinja2 templates sparingly and keep them readable; split large templates.

13.4 Terraform (HCL)

  • Run terraform fmt on all .tf files.
  • Use terraform validate and tools like TFLint, Checkov (security/policy), or tfsec.
  • Follow standard module structure (main.tf, variables.tf, outputs.tf, versions.tf, README.md, examples/).
  • Make modules reusable and small; avoid monolithic files.
  • Use variables with types, descriptions, defaults, and validation blocks.
  • Never hardcode secrets or sensitive values; use data sources or variables.
  • Prefer data sources over static values.
  • Keep resources organized logically; group related resources.
  • Use remote state (with locking); avoid local state in teams.
  • Pin provider and module versions.
  • Add meaningful outputs and use terraform plan reviews in CI.
  • Document modules thoroughly.

13.5 CI/CD Pipeline Code (YAML — GitHub Actions, GitLab CI, Azure DevOps, etc.)

  • Treat pipelines as code — store in repo (.github/workflows/, .gitlab-ci.yml, etc.).
  • Use templates / reusable workflows/jobs for DRY (include/extends).
  • Define clear stages or jobs with dependencies.
  • Keep jobs small and focused (one responsibility).
  • Use caching, artifacts, and parallelization for speed.
  • Pin actions/workflows to specific versions (e.g., @v4, not @latest).
  • Handle secrets securely (never echo them); use OIDC where possible.
  • Add failure notifications, timeouts, and retry logic for flaky steps.
  • Include linting, security scanning (SAST, dependency), tests, and IaC validation in every pipeline.
  • Make pipelines idempotent and reproducible.
  • Use matrix strategies for multi-platform testing.
  • Document pipeline purpose and triggers in comments or README.

13.6 YAML Best Practices (Ansible, Pipelines, Kubernetes, etc.)

  • Use 2-space indentation consistently (never tabs).
  • Keep files readable — limit nesting depth; split large files.
  • Quote strings when needed (especially those with special characters).
  • Use anchors (&) and aliases (*) for reuse, but sparingly to maintain clarity.
  • Validate YAML syntax before commit (tools: yamllint, prettier, or IDE).
  • Prefer YAML over JSON for human-edited files (more readable, supports comments).
  • Order keys logically (e.g., metadata, spec, then status in K8s).
  • Use meaningful comments for complex structures.

13.7 JSON Best Practices (Configs, API payloads, etc.)

  • Use consistent formatting (2 or 4 spaces; enforce with Prettier or jq).
  • Keep files small and focused; split complex configs.
  • Avoid deep nesting; flatten where possible.
  • Use schema validation (JSON Schema) for critical configs.
  • Prefer YAML for new human-maintained configs unless JSON is required (e.g., some tool formats).
  • Minify only for production/runtime if size matters; keep source readable.
  • Never commit secrets or sensitive data in JSON.

14. Tools & Automation for DevOps/IaC (Add to original Tools section)

  • Bash: shellcheck, shfmt, bats
  • Ansible: ansible-lint, molecule (testing), ansible-playbook --syntax-check, yamllint
  • Terraform: terraform fmt, terraform validate, tflint, tfsec/Checkov, terraform-docs, Terratest
  • YAML/JSON: yamllint, prettier, jq, JSON Schema validators
  • Pipelines: Built-in linting + tools like actionlint (GitHub), hadolint (Dockerfiles)
  • General IaC: Trivy, Snyk, or OPA/Conftest for policy-as-code; integrate into CI
  • Enforce via pre-commit hooks, EditorConfig, and CI quality gates (fail on lint/validation errors)

15.1 Branch Naming Conventions

  • Use lowercase letters only.
  • Separate words with hyphens (-). Never use spaces, underscores, or camelCase.
  • Keep names short but descriptive (ideally 3–5 words, max ~50 characters).
  • Always prefix with a type to indicate purpose.
  • Optionally include a ticket/issue ID (e.g., PROJ-123) for traceability.
  • Delete branches after merging (except long-lived ones like main).
PrefixUsageExample
feature/New features or enhancementsfeature/add-user-authentication
fix/ or bugfix/Bug fixes (non-urgent)fix/PROJ-456-login-crash
hotfix/Urgent production fixeshotfix/security-vulnerability
refactor/Code improvements without behavior changerefactor/improve-terraform-module-structure
chore/Maintenance, dependencies, build changeschore/update-ansible-lint-rules
docs/Documentation onlydocs/update-contributing-guide
test/Adding or improving teststest/add-pipeline-integration-tests
ci/CI/CD pipeline changesci/optimize-github-actions-cache

15.2 Commit Messages

We strongly recommend Conventional Commits. This standard makes commits machine-readable, enables automated changelogs, semantic versioning, and better history navigation.

Format

text

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
  • Type (required, lowercase):
    • feat — New feature (minor version bump)
    • fix — Bug fix (patch version bump)
    • refactor — Code change that neither fixes a bug nor adds a feature
    • chore — Maintenance, build, dependencies, etc.
    • docs — Documentation changes
    • style — Formatting, whitespace, etc. (no logic change)
    • test — Adding or updating tests
    • ci — CI/CD pipeline changes
    • perf — Performance improvements
    • revert — Reverting a previous commit
  • Scope (optional): What is affected (e.g., terraform, ansible, pipeline, auth, ui).
  • Description: Short, imperative, present tense (e.g., "add user login" not "added" or "adds"). No period at the end. Max ~50–72 characters.
  • Body (optional): More details, motivation, or context. Wrap at 72 characters.
  • Footer (optional):
    • BREAKING CHANGE: for major incompatible changes
    • Closes #123 or Refs PROJ-456 for issue linking
    • Any other metadata

Examples

Good:

text

feat(terraform): add VPC module with public/private subnets

Implements new networking foundation for staging and prod environments.
Uses remote state backend with DynamoDB locking.

Closes PROJ-789

text

fix(ansible): correct package state in webserver role

Was using `state: installed` which is not idempotent on some distros.
Now uses `state: present`.

Refs #234

text

chore: update dependencies

Ran `npm update` and `terraform init -upgrade`.

text

refactor(pipeline): simplify GitHub Actions workflow

Extracted common steps into reusable composite action.

Bad:

  • fixed bug (vague, no type)
  • Update README.md (not imperative, too generic)
  • feat: Added user authentication feature which is really cool and does a lot of things (too long, past tense)

15.3 Additional Git Best Practices

  • One logical change per commit — Keep commits small and focused.
  • Write meaningful commit messages even for small changes.
  • Squash or rebase messy WIP commits before merging (unless using merge commits for release branches).
  • Use git pull --rebase (or configure it as default) to keep history clean.
  • Protect main/master branch in repository settings (require PRs, status checks, and approvals).
  • Tag releases using semantic versioning (e.g., v1.3.0) when cutting releases. Combine with Conventional Commits for automated versioning.
  • Avoid committing large binaries, generated files, or secrets (use .gitignore and tools like git-secrets or pre-commit hooks).
  • Review git history regularly (git log --oneline --graph).

15.4 Tools & Enforcement for Git

  • commitlint + husky — Enforce Conventional Commits on every commit.
  • pre-commit framework — Run git hooks for linting, formatting, and branch name checks.
  • semantic-release — Automate versioning and changelogs based on commits.
  • Conventional Commits extensions for VS Code, IntelliJ, etc.
  • Add a .gitmessage template or commit.template for easier formatting.

Document any intentional deviations (rare) with a clear reason in the commit body or PR description.