Database Triggers
debt(d7/e7/b7/t7)
Closest to 'only careful code review or runtime testing' (d7). Database triggers are invisible to application code and standard PHP tooling. The detection_hints list mysql-workbench and pgadmin as tools, but these are database admin tools that require manual inspection of database schema — they don't automatically flag problematic triggers. No automated detection exists per the term metadata. Developers typically discover trigger-related issues only through careful code review of the database schema or when debugging unexpected runtime behaviour.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix says to 'use PHP application events instead for audit logging and side effects' — this is not a one-line fix but requires architectural migration. Moving business logic from database triggers into application-level event handlers requires: identifying all triggers, understanding their logic, implementing equivalent PHP event listeners, updating all relevant code paths, and ensuring data consistency during migration. This spans multiple files and often crosses component boundaries.
Closest to 'strong gravitational pull' (b7). Triggers operate invisibly at the database layer, affecting all code that touches the relevant tables regardless of context (web, cli per applies_to). The common_mistakes highlight 'cascading triggers' and logic that is 'invisible to application developers' — this creates a persistent tax where every developer must remember triggers exist when working with affected tables. The misconception notes triggers 'hide logic that developers must know exists', meaning they impose cognitive load on all future maintainers working with those tables.
Closest to 'serious trap' (t7). The misconception explicitly states 'Triggers are good for keeping derived data in sync' as the canonical wrong belief, when actually 'the outbox pattern or application-level events are more transparent and testable'. This contradicts how developers expect cause-and-effect to work — they expect application code changes to be traceable in application code. The why_it_matters describes 'a developer deletes a row, three other rows change via trigger' as a debugging nightmare because the side effects are hidden from normal code inspection.
Also Known As
TL;DR
Explanation
Triggers execute SQL logic automatically when data changes. Common legitimate uses: audit trails (log who changed what and when), denormalised counter updates (post_count on user table), enforcing complex constraints not expressible as check constraints, and maintaining summary tables. Dangers: invisible side effects (application writes one row, trigger writes five more), performance surprises (a simple INSERT triggers complex logic), testing difficulty (tests must set up trigger state), and debugging complexity (unexpected data changes have invisible causes).
Diagram
sequenceDiagram
participant APP as PHP App
participant DB as Database
participant TRG as Trigger
participant LOG as Audit Log
APP->>DB: UPDATE orders SET status='shipped'
DB->>TRG: AFTER UPDATE fires automatically
TRG->>LOG: INSERT into order_audit_log
TRG-->>DB: complete
DB-->>APP: 1 row updated
Note over APP,TRG: App wrote 1 row<br/>trigger wrote 1 more<br/>developer may not know
Common Misconception
Why It Matters
Common Mistakes
- Complex business logic in triggers — impossible to unit test and invisible to application developers.
- Triggers that call external services — synchronous triggers block the transaction; use the outbox pattern.
- Cascading triggers — trigger A fires trigger B which fires trigger C; debugging is nearly impossible.
- Not documenting that triggers exist — future developers modify the table unaware of side effects.
Code Examples
-- Trigger hiding business logic:
CREATE TRIGGER after_order_insert
AFTER INSERT ON orders FOR EACH ROW
BEGIN
-- Sends email (makes HTTP call from trigger!)
-- Updates inventory across 3 tables
-- Calculates customer loyalty points
-- Updates denormalised reporting tables
END;
-- Developer INSERT INTO orders... has no idea this fires
-- Trigger for legitimate audit only:
CREATE TRIGGER audit_order_updates
AFTER UPDATE ON orders FOR EACH ROW
BEGIN
INSERT INTO order_audit_log
(order_id, old_status, new_status, changed_at, changed_by)
VALUES (OLD.id, OLD.status, NEW.status, NOW(), @current_user);
END;
-- Business logic stays in PHP where it's testable and visible