Best Practices, Style & Common Pitfalls

43.1 Coding style

Follow the Embarcadero Object Pascal style guide conventions: 2-space indentation, begin on its own line, PascalCase identifiers, T/I/E/F/A prefixes.

Keep keywords lowercase (begin, end, procedure); write identifiers consistently even though Pascal is case-insensitive.

One class per unit for large classes; group small related types.

Put uses in the implementation section whenever possible.

Prefer const parameters for strings, records, and interfaces.

Use strict private for real encapsulation; expose state through properties.

Write try/finally immediately after creating an object.

Prefer Exit(Value) and early returns over deep nesting; avoid goto and with.

Name booleans as predicates (IsEmpty, HasChildren, CanClose).

Turn on hints and warnings and keep the build clean.

43.2 Common pitfalls

Missing parentheses in if A > 0 and B > 0 compile error or wrong logic

if (A > 0) and (B > 0) — and binds tighter than > Semicolon before else "';' expected but ELSE found" remove the semicolon Using = for assignment compiles as a comparison (or error)

Uninitialized local variables / Result initialize every local; set Result on all paths Forgetting Free

try/finally, owners, heaptrc/FastMM reports Using an object after Free access violations, random crashes FreeAndNil, single ownership Mixing object and interface references premature destruction or leaks use only interface references for ref-counted objects

Static method "overridden" without virtual/override base method called mark base virtual, descendants override

Missing inherited in constructors/destructors uninitialized ancestor state, leaks call inherited Create first, inherited Destroy last

Assuming dynamic array assignment copies changes visible through both variables

Mixing 1-based Copy/Pos and 0-based string helpers choose one style

string means different things ({$H}, mode, Delphi version) truncated strings, encoding bugs set {$mode}/{$H+} explicitly; know your Char size

Treating Length of a UTF-8 string as a character count wrong lengths, broken substrings UTF8Length/UTF8Copy in Lazarus Modifying a collection during for-in exceptions, skipped items iterate backwards with an index

Accessing UI controls from a worker thread Forgetting cthreads on Unix (FPC)

"This binary has no thread support compiled in" put {$IFDEF UNIX}cthreads,{$ENDIF} first in the program's uses Integer overflow silently wrapping {$Q+} in debug builds; use Int64 Floating-point equality (X = 0.1) SameValue(A, B, Epsilon) from Math

Typed constants unexpectedly writable/read-only compile errors or surprising state know {$J} defaults (FPC on, Delphi off) SQL built by concatenating user input

43.3 Project checklist

Explicit compiler mode and directives at the top of every unit (or in an include file).

Debug build with checks and heaptrc/FastMM leak reporting; release build optimized.

Business logic in testable units, separate from forms.

Unit tests (FPCUnit/DUnitX) running in CI (GitHub Actions/GitLab CI with FPC or Delphi build agents).

Consistent formatting (JEDI Code Format / Delphi formatter).

Version control with .lfm/.dfm stored as text; ignore build outputs (lib/, *.ppu, *.o, *.dcu, __history).