Menu

Topic 4 of 7

Maintainable Code Standards

Learn Maintainable Code Standards for free with explanations, exercises, and a quick test (for Backend Engineer).

Published: January 20, 2026 | Updated: January 20, 2026

Who this is for

Backend engineers who want their code to be easy to read, change, review, and operate in production. Useful for solo developers, small teams, and large orgs.

Prerequisites

  • Basic proficiency in at least one backend language.
  • Familiarity with Git (branching, commits, pull requests).
  • Ability to run tests locally.

Why this matters

Maintainable code standards reduce bugs, speed up reviews, and make on-call life calmer. Real tasks where this matters:

  • Reviewing pull requests and understanding changes quickly.
  • Debugging incidents at 2 a.m. with helpful logs and clear structure.
  • Safely evolving APIs and database schemas without breaking clients.
  • Onboarding new teammates without endless walkthroughs.

Concept explained simply

Maintainable code is code that another engineer can safely modify after reading it once. Standards are the shared rules that make this possible—naming conventions, structure, error handling, tests, docs, and automation.

Mental model: the 2 a.m. teammate

Imagine a teammate fixing a production issue at 2 a.m. Ask: Can they find the entry point? Understand intent from names? Trust tests? Follow logs to the root cause? Roll back safely?

The "7S" baseline (quick checklist)
  • Small functions and files.
  • Single responsibility per module.
  • Self-documenting names + minimal, precise comments.
  • Standard project structure.
  • Safe changes via tests and migration/rollback plans.
  • Sound error handling and structured logging.
  • Style automation (formatter + linter) in CI.

Worked examples

1) Refactor for clarity and safety

Before
def p(u):
    r = db.get(u)
    if r != None:
        if r['st'] == 'a':
            r['pts'] += 1
            db.save(u, r)
            return r
        else:
            return None
    else:
        return None
After (maintainable)
from typing import Optional, Dict

class NotActiveError(Exception):
    pass

def increment_points(user_id: str) -> Dict:
    """Increment points for an active user.

    Raises:
        KeyError: when user not found
        NotActiveError: when user exists but is not active
    """
    user = db.get(user_id)
    if user is None:
        raise KeyError(f"user not found: {user_id}")
    if user["st"] != "a":
        raise NotActiveError(f"user not active: {user_id}")

    user["pts"] += 1
    db.save(user_id, user)
    logger.info("points_incremented", extra={"user_id": user_id, "points": user["pts"]})
    return user
  • Clear names and types.
  • Early returns/exceptions, explicit errors.
  • Structured log with context.
  • Docstring sets expectations.

2) Consistent API errors and logs

Pattern
# Response shape
{
  "error": {
    "code": "NOT_ACTIVE",
    "message": "User is not active"
  }
}

# Logging shape
logger.warning("api_error", extra={
  "route": "/users/:id/points",
  "code": "NOT_ACTIVE",
  "user_id": user_id
})

Keep error codes stable for clients, and ensure logs include route, code, and key IDs.

3) Commit + PR standards

Example
feat(points): increment only for active users

- Add NotActiveError and explicit error handling
- Return stable API error code NOT_ACTIVE
- Log with structured context (route, code, user_id)
- Tests: happy path, not found, not active

PR description includes what changed, why, and how you tested it.

4) Safe database migrations

Example
# 2026-01-20_add_points_default.sql
ALTER TABLE users ADD COLUMN points INT NOT NULL DEFAULT 0;
-- Rollback plan
-- ALTER TABLE users DROP COLUMN points;
  • Name includes date and purpose.
  • Default value avoids NULL surprises.
  • Rollback plan documented.

Set up your team standards (step-by-step)

Step 1 — Decide on structure

Choose a project layout (src/, tests/, migrations/, docs/). Add a short README describing the layout.

Step 2 — Automate style

Pick a formatter and linter. Add them to CI so pull requests must pass.

Step 3 — Define API error conventions

Pick stable error codes and response shape. Document in docs/errors.md.

Step 4 — PR and commit templates

Create a PR template and a commit message guideline. Include test plan and risk notes.

Step 5 — Testing and migrations

Require tests for new logic. For schema changes, include forward + rollback scripts.

Quick checklists

Before you push

  • [ ] Code formatted; linter passes.
  • [ ] Function and variable names reflect intent.
  • [ ] Logs include IDs and context, no secrets.
  • [ ] Errors return stable codes/messages.
  • [ ] Tests cover happy path + main edge cases.
  • [ ] README or docs updated when behavior changes.

Pull request

  • [ ] Title states what changed.
  • [ ] Description explains why and how you tested.
  • [ ] Risk/rollback plan noted (especially for migrations).
  • [ ] Small, focused diff (or clearly scoped if large).

Exercises you'll do

  1. Exercise 1: Refactor a messy function to meet standards (naming, errors, logging, docstring, types). Add a small test outline.
  2. Exercise 2: Write a standards-compliant commit message and PR description for an API change with tests and risk notes.

See the Exercises section below for full instructions and solutions.

Common mistakes and self-check

  • Monster functions: If a function is hard to name, it probably does too much. Split it.
  • Inconsistent names: Use the same terms throughout (e.g., user_id vs uid). Search your codebase to confirm consistency.
  • Comments instead of code: Prefer clear names and small functions. Keep comments for why, not what.
  • Unstructured logs: Free-text logs are hard to query. Add key-value context (IDs, codes).
  • Hidden side effects: Make state changes explicit and documented. Avoid surprising global mutations.
  • No rollback plan: For migrations and risky changes, always include how to revert.
Self-check mini audit (5 minutes)
  • Pick one file. Can you summarize each function in one sentence?
  • Find one log line. Does it include enough context to trace a request?
  • Open a recent PR. Does the description explain risk and testing?

Practical projects

  • Create a short code standards doc for your repo (one page). Include naming, logging, errors, tests, and PR template.
  • Refactor one business-critical function using the 7S baseline. Add tests and logs.
  • Set up formatter + linter + tests in CI. Make the pipeline fail on style or test issues.
  • Write an Architecture Decision Record (ADR) for error code conventions.

Learning path

  1. Adopt a formatter and linter; fix a few files.
  2. Define error and logging conventions; update one endpoint.
  3. Add tests for critical paths; measure confidence by removing a bug.
  4. Create PR and commit templates; review two PRs using them.
  5. Document standards in the repo; keep it short and enforced.

Next steps

  • Apply these standards to a new feature end-to-end.
  • Host a short team review to agree on any tweaks.
  • Automate enforcement in CI for consistency.

Mini challenge

In 20 minutes, take a small module and:

  • Rename variables and functions for clarity.
  • Add one structured log and one explicit error.
  • Write a two-sentence README note about how to modify it safely.

Quick test

Take the short test to check your understanding. Available to everyone; only logged-in users get saved progress.

Practice Exercises

2 exercises to complete

Instructions

Refactor the function below. Goals:

  • Clear, intention-revealing names.
  • Docstring with inputs, outputs, and errors.
  • Explicit error handling (no silent None returns).
  • Structured logging with key context.
  • Type hints.
Starter code
def upd(u, p):
    d = db.get(u)
    if not d:
        return None
    if p < 0:
        return None
    d['pts'] = d.get('pts', 0) + p
    db.save(u, d)
    print('ok')
    return d

Add a tiny test outline with three cases: user not found, negative points, success.

Expected Output
A refactored function with clear naming, docstring, explicit exceptions for not found/invalid input, structured logging, and a brief test outline.

Maintainable Code Standards — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about Maintainable Code Standards?

AI Assistant

Ask questions about this tool