Engineering Fundamentals Handbook

Purpose

Engineering is the disciplined application of knowledge to build and maintain software systems.

This handbook defines the enduring principles, standards and decision frameworks that guide engineering work within this organization.

Unlike technology-specific guides, this document focuses on fundamentals that remain valuable regardless of programming language, framework or tooling choices.

Its primary objective is to improve engineering judgement.

Engineers who understand these principles can make better decisions, produce higher quality software and collaborate more effectively with both humans and AI.

Scope

This handbook covers:

Technology-specific guidance belongs in capability-specific handbooks (e.g., Rails Engineering, Software Architecture).

Principles

Separation of Concerns

Every module, class, function or component should have a single, clearly defined responsibility.

Code that attempts to do many things simultaneously is harder to understand, harder to test and harder to change.

Applying separation of concerns means:

See the Engineering Glossary for definitions of coupling and cohesion.

Encapsulation

Components should hide their internal implementation details behind a stable interface.

Encapsulation enables change by ensuring that internal refactoring does not affect consumers.

Well-encapsulated code:

Composition over Inheritance

Prefer composing behaviours from small, focused units over building deep inheritance hierarchies.

Composition is more flexible, easier to test and less likely to produce fragile code.

Inheritance is appropriate when:

When in doubt, prefer composition.

Dependency Inversion

High-level modules should not depend on low-level modules.

Both should depend on abstractions.

This principle enables:

Apply dependency inversion at architectural boundaries.

Explicit Dependencies

Components should declare their dependencies explicitly rather than obtaining them implicitly.

Explicit dependencies:

Prefer dependency injection, constructor injection or service locators over global state or static access.

Consistency

Consistent code is easier to read, review and maintain.

Consistency applies to:

Follow the conventions established in the capability-specific handbooks. When no convention exists, establish one rather than introducing a new style.

Defend against Complexity

Software complexity is the primary driver of long-term cost.

Complexity manifests as:

Defend against complexity by:

Simplicity is not simplicity of implementation.

It is simplicity of understanding.

Prefer Clarity over Cleverness

Code is read far more often than it is written.

Optimise for the reader.

Clear code:

If code is difficult to understand, it is difficult to maintain.

Code Organization

Module Boundaries

Organise code into modules with clear responsibilities.

Each module should:

Layer Separation

Separate code into logical layers.

Common layers include:

Each layer should depend only on the layer beneath it.

Package by Feature

Organise code by feature rather than by layer.

Feature-based organisation:

Engineering Standards

Naming

Names should reveal intent.

Good names:

GoodAvoid
UserRegistrationServiceUserService
send_password_reset_emailhandle_email
calculate_total_revenuecalc

Error Handling

Errors are a normal part of software operation.

Engineers should:

Logging

Logging should support operations.

Effective logging:

Testing

Every capability should define its testing standards.

Common principles include:

See the Code Review Playbook for testing review standards.

Quality Philosophy

Define Done

Every engineering task should have a clear definition of done.

A task is complete when:

Technical Debt

Technical debt is the gap between the current state of the code and the desired state.

Some technical debt is strategic: taken intentionally to achieve a business outcome with a plan to repay.

Some technical debt is accidental: accumulated through neglect, inadequate design or insufficient testing.

Engineers should:

Refactoring

Refactoring is the disciplined practice of improving code structure without changing behaviour.

Refactoring should:

Regular refactoring prevents gradual decay.

Decision Frameworks

When to Introduce Abstraction

Ask these questions before introducing a new abstraction:

  1. Does the abstraction simplify the code?
  2. Is the abstraction likely to be reused?
  3. Can we demonstrate at least three concrete use cases?
  4. Does the abstraction hide genuine complexity?

If the answer to most questions is no, defer the abstraction.

When to Accept Technical Debt

Technical debt may be acceptable when:

Technical debt is never acceptable when:

When to Rewrite

Rewriting a system carries significant risk.

Consider a rewrite only when:

Prefer incremental improvement over wholesale replacement.

AI Integration

AI as Collaborator

AI systems should be treated as engineering collaborators.

They can assist with:

Human Responsibility

Human engineers remain responsible for all engineering decisions.

AI-generated code, designs or documentation must be reviewed by a human before acceptance.

AI can accelerate engineering work, but it cannot replace engineering judgement.

Context for AI

AI systems are more effective when provided with:

See the AI Engineering Handbook for detailed guidance on AI workflows and context engineering.

Capability Map

This handbook is the entry point for the Engineering Fundamentals capability. The following documents form the complete capability:

Handbooks

Glossaries

Guides

Learning Paths

References

AI Workflows

Related Documents