Appearance
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).
Recommended Branch Prefixes
| Prefix | Usage | Example |
|---|---|---|
| feature/ | New features or enhancements | feature/add-user-authentication |
| fix/ or bugfix/ | Bug fixes (non-urgent) | fix/PROJ-456-login-crash |
| hotfix/ | Urgent production fixes | hotfix/security-vulnerability |
| refactor/ | Code improvements without behavior change | refactor/improve-terraform-module-structure |
| chore/ | Maintenance, dependencies, build changes | chore/update-ansible-lint-rules |
| docs/ | Documentation only | docs/update-contributing-guide |
| test/ | Adding or improving tests | test/add-pipeline-integration-tests |
| ci/ | CI/CD pipeline changes | ci/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-789text
fix(ansible): correct package state in webserver role
Was using `state: installed` which is not idempotent on some distros.
Now uses `state: present`.
Refs #234text
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.