Editorial pass per user direction: stop justifying the architecture.
For every "why this design works" passage, name the costs the design
imposes — not as a parenthetical aside but as substantive critical
analysis. Each major architectural-claim page now carries an explicit
drawbacks/costs section.
Pages revised:
concepts/thesis.md
- "The reward" → "What the design enables (and what each enabler still costs)":
for each promised benefit (single codebase, PMs evolve without
engineering, customisations layered cleanly), name the limit. Added
closing observation that data-driven design redistributes complexity
to people and tools the framework can't compile-check.
- "When it breaks down": rewrote to call out that "bypassing the
framework" via 18 customer dirs makes the data-driven thesis
partial, not complete.
concepts/semantic-fk.md
- "Why xly disabled FKs": added critical analysis. Both reasons
could be addressed surgically; the chosen "no FKs anywhere" is the
trade for DB-enforced integrity, paid every day the system runs.
concepts/master-slave.md
- "Slave naming caveat": stop framing retention as wise pragmatism.
The naming was a poor choice; preservation has a real ongoing cost.
concepts/modules-forms-vtables.md
- "Three nouns, one engine": the universal dispatch path concentrates
3,500+ lines + edge cases + special-case hardcodes in one class.
Naming the trade.
concepts/multi-tenancy.md
- "How the design scales" → "How the design scales — and where it
doesn't": shared schema = shared contention; tenant-filter index
discipline; no physical hard-delete; rigid (sBrandsId,
sSubsidiaryId) tenancy unit.
concepts/customization-channels.md
- Soften "90%+ should live here" claim — that's an aspirational
target, not a measured fact. The 18 customer override directories
are evidence the channel-2 demand is non-trivial.
concepts/api-surface.md
- "Why three tiers, not one" → "Why three tiers (and what splitting
them costs)": three WARs to deploy, duplicate code, no shared
session, three reverse-proxy entries. Note the alternative
(single-WAR with package boundaries) and what that would cost
vs gain.
reference/maintainer/proc-dispatch.md
- "Why dynamic proc dispatch matters": added five concrete costs
(no compile-time check, no type safety, no call-site discoverability,
no static analysis, broken stack traces). Reframed: dynamic
dispatch made it cheap to keep adding procs, which made the pile
grow, which made the pile harder to audit.
reference/maintainer/cache-invalidation.md
- New "Drawbacks of this design" section: confusing co-named systems,
eviction in same transaction as write (silent corruption on
Redis outage), allEntries=true blunt eviction, no batching,
direct DB writes bypass everything. Also fixed the "if cache is
local" hedge in section 3 (we've now empirically confirmed Redis-
backed, so cache is shared).
reference/maintainer/bi-engine.md
- New "Drawbacks of the homebrewed approach" section: every chart
needs a SQL author, charts run heavy SQL on OLTP DB, no semantic
consistency between charts, no drill-down, customer-divergent KPI
logic. Also dedup'd the duplicated "What this is not" section.
reference/maintainer/sql-templates.md
- "Why this is a 'template' library and not a code generator" →
added costs: no enforcement, no regeneration, no template-origin
tracking, customer overrides drift from scaffold. The 1,687 procs
the schema carries are the evidence that "discipline rather than
enforcement" doesn't fully hold.
reference/maintainer/activiti.md
- "Why this design works for xly's audience" → "Why xly avoided
Activiti — and what that costs": scattered workflow logic, no
central audit trail, no parallel-branch/reassignment, invisible
flow-graph evolution, idle Activiti engine paying boot cost
anyway.
- "Why xly bothered with Activiti at all" → "Why xly bothered with
Activiti — and whether it was worth it": named the costs (second
engine, second schema, second auth surface, modeler UI to learn)
and the damning fact that on this dev DB the engine is idle. A
future cleanup could plausibly remove Activiti entirely.
reference/maintainer/runtime.md
- New "What 'universal CRUD' means in practice" section: 3,500-line
single-point-of-failure class, no type system on Map<String,Object>,
poor discoverability ("what endpoints write to table X" is
unanswerable). The trade: adding a module is essentially free,
touching the runtime essentially never is.
- Updated cache-invalidation cross-link to drop the "open question"
hedge (now empirically resolved).
slices/04-custom-field.md
- "Why it works without code changes" → "Why it works without code
changes — and what that costs": merge runs on every request,
three near-empty tables on every schema, display-only extension
(real persisted fields still need ALTER TABLE), debuggability
requires diffing 3 overlay tables.
slices/05-customer-sql-override.md
- Added drawbacks: no version control on the deployed body, no
type-safety bridge, compounds the BI problem. Reframed the
"right rule of thumb": 18 customer override directories suggest
the channel-2 demand is structural, not exceptional — that's
evidence the metadata model isn't expressive enough, not a
celebration of the escape hatch.
slices/06-hardware.md
- "The cleanest story xly tells about an awkward problem" →
removed the "cleanest" framing. Added costs of "DB as the only
contract": no backpressure, no request/response, bridge-side
state invisible to the framework, three layers of polling
multiply latency, hardest code (byte protocols) gets least CI.
A real-time-aware architecture would use streaming end-to-end;
xly's choice trades latency, observability, flow control for
operational simplicity. Liveable for press tempo, not for
faster shop-floor signals.