Bad Code and Anti-Patterns
This catalog describes code that is considered bad practice and anti-patterns that must be avoided. These patterns are often introduced under time pressure, through shortcuts, or due to lack of experience. While they may appear convenient, they degrade code quality over time. Whenever code is modified or housekeeping is performed, such patterns must be removed or—if truly unavoidable—clearly documented, including why they cannot be avoided.
Severity
Each anti-pattern has one of these severities:
high: Must not exist in our code base, unless it is a documented, well-reasoned exception.
medium: Should not exist in our code base, but fixes can be postponed if the quality impact is low.
low: Should not exist in our code base and can be fixed in dedicated housekeeping sessions.
Automated Scanner
Run the agent-focused scanner from the project root:
.venv/bin/python3 utilities/run.py anti_patterns
The scanner reports the most severe findings first and limits its output to 20 report rows by default.
Within one severity, rows representing more findings are shown first.
Multiple missing API documentation or default-group comment findings in the same file are grouped into one row that
shows the first location and the number of additional findings.
Pass one or more project-relative files or directories to narrow the scan, use --limit to change the row limit, and
use --show-suppressed to audit accepted locations.
Each report ends with paths to the relevant pages in this catalog.
The scanner uses conservative mechanical heuristics.
A finding is a request to inspect the location, not proof that the code is wrong.
Findings inside conditional compilation blocks opened with an ERBSLAND_OS macro are ignored because these blocks
contain platform-specific integration code that must be inspected in its native API context.
Source-level findings are cached in .cache/anti_patterns.json.
Unchanged files are not read or parsed again, while changes to a source file or to the scanner implementation
automatically invalidate the affected cached results.
Accepted Locations
Prefer fixing a finding. If a construct is unavoidable, accept only the narrowest possible scope and always explain why. A single location can be accepted with an adjacent line comment before the construct or after its statement:
// anti-pattern: allow static_cast_void -- The discarded result is safe because validation already succeeded.
static_cast<void>(operation());
static_cast<void>(operation()); // anti-pattern: allow static_cast_void -- Validation already succeeded.
The marker must use the exact rule identifier and include a non-empty reason.
For a directory- or file-wide exception, add a rule entry to utilities/conf/anti_patterns.elcl.
The excluded path is relative to the project root.
A directory path recursively covers its contents.
Central exceptions require a reason and remain visible with --show-suppressed.
--*[ Rule . static_cast_void ]*--
Excluded Path : "test/unittest/src"
Reason : "The guideline explicitly permits this construct in unit tests."
If an excluded path contains * or ?, it is matched as a case-sensitive path pattern against each
project-relative path.
Wildcards do not cross directory separators; use ** to match any number of directories.
--*[ Rule . static_only_class ]*--
Excluded Path : "src/erbsland/unit/*Unit.hpp"
Reason : "These unit traits define compile-time representation mappings."
Anti-Pattern Catalog
- Files Longer Than 500 Lines
- Anonymous Namespaces
- Classes and Structs in the Wrong Files or Units
- Namespaces in the Wrong Units
- Implementations in the Wrong Units
- Oversized Nested Types
- Forward Declarations at the Usage Location
- Static Global Object Construction
- Classes with Only Static Methods
- Missing API Documentation
- Missing Default Group Comment
- Multiple Classes, Structs, or Enums in a Header
- Regular Literal Strings for Erbsland Core APIs
- Silencing nodiscard with
static_cast<void>(...) - Inefficient and Unnecessary Conversions or Copies
- Nested Namespace Blocks and End Comments