A Framework for Singular Ownership in Complex Software Systems
“A fix made in one place should propagate everywhere it is needed, automatically, without the developer having to know everywhere it is needed.”
— The founding premise of this architectureEvery rule here exists because something broke without it. Every axiom was earned through production software — not designed in advance.
NACODEX describes structural rules for organizing software systems — particularly systems that grow feature by feature, serve multiple components from shared resources, and must be maintainable without losing track of where everything lives.
It was developed through shipping software, encountering failure, identifying the structural cause, and correcting not just the symptom but the condition that made the symptom possible.
The framework applies to any software system where multiple components share access to the same underlying resources, the same logical operation is needed in more than one place, and a single developer or small team must maintain the whole.
The principles are not platform-specific. The architecture originated in Android mobile development. The rules are general.
Integrates the base Codex, Update 1 (ICVU pattern), and Update 2 (Fixed Surface Rule). Expand any section to read.
This document describes an architectural framework developed empirically — through shipping software, encountering failure, identifying its structural cause, and correcting not just the symptom but the condition that made the symptom possible.
The framework applies to any software system where multiple components share access to the same underlying resources; the same logical operation is needed in more than one place; the system grows feature by feature over time; and a single developer or small team must maintain the whole without losing track of it.
This document serves two purposes simultaneously: it is a template for building or refactoring a project to follow this architecture, and it is an academic study of what happens when you take the question “where should this function live?” seriously enough to answer it with permanent, enforceable rules rather than intuition.
This document is not final. It is current.
NACODEX is updated whenever an architectural discovery warrants it. Every rule reflects the best understanding at the time of its integration. A later update may refine a rule’s domain, add conditions to its application, or define its boundary with another rule. This is expected and correct behaviour — not a sign that the earlier rule was wrong.
Every software project that grows feature by feature eventually encounters the same failure mode: divergent duplication. A function is written in Class A. Later, Class B needs it and copies it with slight adaptations. Two independent copies now exist, maintained independently. A bug fixed in one is not fixed in the other — because the other is not remembered.
This is not a failure of discipline. It is a structural failure. The codebase has no mechanism to prevent duplication and no mechanism to enforce that a fix propagates. The framework in this document solves this structurally by ensuring every function, constant, and piece of logic has exactly one home.
Empirical observations derived from observation rather than theory. The rules that follow are grounded in these premises.
Behavioral rules that all code following this framework obeys. A violation is a defect, not a style difference.
addView() time to the maximum content it will ever display. After addView(), window dimensions are never changed. updateViewLayout() is called with position changes only. Content size changes are reflected through invalidate() and onDraw(), not through surface reallocation. Rationale: updateViewLayout() with new dimensions triggers surface reallocation in the compositor. The reallocation and the next completed draw are decoupled across frames, producing a positional artifact proportional to the size delta. No application-layer fix can prevent a compositor-layer timing artifact. See Section 2.7 and Dead Ends DE23–DE24.Every function belongs to exactly one of five categories. The test for each is applied in order; the first that fits is correct.
Every class belongs to exactly one layer. Dependencies only flow downward. A violation is always a design error, always fixable by introducing a callback interface or splitting the violating class.
InstantCompactValueUpdate (ICVU) — a write pattern for values that must remain consistent across multiple storage media at all times. Instant: the update happens synchronously. Compact: both media are updated in a single method call. The caller has no knowledge of the two-medium write.
Apply when ALL are true: (1) the value is written by more than one component; (2) read by more than one component; (3) an incorrect read causes a user-visible failure; (4) the failure may occur silently without any error or warning.
// Atomic setter — the only location where both writes occur
private void setValue(float x, float y) {
fieldX = x; fieldY = y; // 1. update in-memory
storage.save(this, x, y); // 2. update persistent storage
updateDisplay(); // 3. optional: refresh derived display
}
// Sync-from-storage — called at component resume points
private void syncFromStorage() {
fieldX = storage.getX(this, fieldX);
fieldY = storage.getY(this, fieldY);
updateDisplay();
}
Domain boundary (Update 2): ICVU governs application-layer value consistency only. Window dimensions live in the compositor domain and are governed by AX12 instead.
The problem: An overlay window whose content changes size at runtime produces a diagonal positional artifact on every resize call, equal to the size delta per axis. The artifact is in the system compositor — not in application code. No application-layer fix can prevent a compositor-layer timing artifact.
The rule: Size the window once at addView() to the maximum content it will ever display. Only x and y change through updateViewLayout(). Content size changes via invalidate() only.
// Attach — size ONCE at maximum content dimensions
int fixedSize = maxContentSize;
if (fixedSize % 2 == 0) fixedSize += 1; // force odd → half-integer center
params = buildParams(centerX, centerY, fixedSize);
windowManager.addView(view, params); // surface allocated once
// Content size change (slider, animation, theme)
content.update(newSize, newRadius); // sets fields, calls invalidate()
// NO updateViewLayout — surface NOT reallocated
// Position change only
params.x = (int)(targetCenterX - fixedSize / 2f);
params.y = (int)(targetCenterY - fixedSize / 2f);
windowManager.updateViewLayout(view, params);
A dead end is a specific approach that is structurally incapable of solving a class of problems, documented to prevent future investigation time from being spent rediscovering that these approaches do not work.
When an approach appears here: this has been investigated fully and eliminated. Do not revisit without a specific new argument that changes the model — not just a different formulation of the same approach.
addView() during an active operation triggers platform system events that may stop the operation. Window creation is guarded: created before the operation starts or after it ends, never during.statusBarHeight, contentHeight, or screenDensity independently produce inconsistent results at boundary conditions. One measurement utility, canonical implementations only.onDraw() pass, all synchronized content drawn together.updateViewLayout() with new width or height on an overlay while it is visible produces a diagonal positional artifact equal to |oldHalfSize − newHalfSize| px in both axes, on every resize call. The artifact is in the system compositor, not in application code. Fix attempts that do not resolve this: correcting coordinate calculations, changing call ordering, reading from measured view size, using a single window, applying ICVU to window dimensions. The only resolution: never change window dimensions after addView(). See AX12 and Section 2.7.updateViewLayout() calls does not guarantee they appear synchronized. The system compositor composites each window’s surface independently. A frame can be rendered with one window updated and the other not yet updated. This is structural, not a timing or ordering problem. The only resolution: merge synchronized visual elements into a single window with one onDraw() pass.The test gap indicator: If a function cannot be tested without instantiating a Category E class, the function is in the wrong place. Extract it to a lower layer.
An architectural rule, when applied to a domain for which it was not designed, may produce correct behaviour in the domain it governs and incorrect behaviour in the adjacent domain it does not govern. The evidence of this situation is: following the rule correctly makes something worse.
When this occurs, the correct response is: (1) do not abandon the rule; (2) define the domain boundary of the rule explicitly; (3) identify the adjacent domain and find or create the governing rule for it; (4) document both the rule and its boundary in the Codex.
This Codex has now accumulated two updates. The ICVU rule (Update 1) was correct and applied broadly. Applied to overlay window dimensions, it produced correct values but worsened visual behaviour through increased compositor write frequency. Update 2 defines the domain boundary of ICVU and adds AX12 to govern the compositor domain. Both rules — ICVU and the Fixed Surface Rule — are now in the Codex with their domains defined. Neither one alone is sufficient. Both are correct.
The rules in this document were not designed in advance. They were extracted from working software after observing what broke and why. What good architecture actually requires is systematic observation and extraction. The developer who watches which functions get copied, which constants get misspelled, which interface boundaries collapse under load — and then responds by extracting, formalising, and enforcing — will produce better architecture than the developer who designs the perfect system in advance and struggles to fit reality into it.
Theory produces elegant architecture. Practice produces correct architecture. The gap between them is smaller than it appears, but it only closes through observation — not through reasoning alone.
Is it a pure computation with no state?
→ Category A. Lives in the foundation utilities file.
Is it a group of related fields that travel together?
→ Category B. Lives in a dedicated data container class with a Builder.
Does it own a resource lifecycle, needed by multiple consumers?
→ Category C. Lives in a dedicated manager class.
Does it wrap a platform API to hide its complexity?
→ Category D. Lives in a dedicated bridge class.
Does it respond to user input or own a visible surface?
→ Category E. Lives in an activity, fragment, or service.
Does it identify a persistent value or inter-component message?
→ It is a constant. Lives in the constants file. Always.
Is it a value written by more than one component?
→ It is an ICVU value. It gets an atomic setter and sync-from-storage method.
[Added in Update 1]
Does this code call updateViewLayout() with new width or height while visible?
→ It violates AX12. Size the window at addView() time to the maximum content
size. Only pass position changes to updateViewLayout() after that.
[Added in Update 2]
A function appears in more than one class?
→ Violation of AX1. Extract to the appropriate category.
A manager imports a concrete consumer class?
→ Violation of AX2. Introduce a callback interface.
A string literal identifies a key or action in more than one file?
→ Violation of AX3. Declare in constants file and reference by name.
A consumer builds its own inter-component message inline?
→ Violation of AX4. Add a method to the appropriate sender class.
A value is written in one place without updating persistent storage?
→ ICVU anti-pattern 1. The value requires an atomic setter. [Update 1]
A value is written to storage without updating the in-memory field?
→ ICVU anti-pattern 2. The value requires an atomic setter. [Update 1]
updateViewLayout() is called with new width or height while visible?
→ Violation of AX12. Resize only at addView(). Use invalidate()
for content changes. [Update 2]
Two overlay windows kept in sync through sequential updateViewLayout calls?
→ Dead End DE24. Merge into a single window. [Update 2]
addView() to the maximum content dimensions and never resized after that. [Added in Update 2]Updates submitted by contributors that have not yet been integrated into the main Codex body. Visually distinct from the integrated content above.
All submitted updates have been integrated into v1.1.2.
New submissions will appear here before integration.
A full record of every edition and integrated update. Older versions are not discarded — rules survive recodification unless explicitly retired with a documented rationale.
Versioning scheme: major.recodification.updates_in_cycle — the third digit increments with each integrated update; the second digit increments and the third resets to zero when updates are consolidated into a full recodification.
NACODEX is a living document. If you’ve found something that belongs here — a pattern, a dead end, a scope boundary — submit it. The Codex grows through production experience, not theory.
A NACODEX update documents a structural discovery — a finding about how software systems behave architecturally, not just a bug fix. It must be generalisable: someone who wasn’t there when you found it must be able to apply it.
A reusable solution structure to a class of problems. Has a name, a defined application condition, and a defined implementation form.
An approach that is structurally incapable of solving the problem. You can explain why it cannot work — not just that it failed.
A factual observation about software systems of this type that, if violated, reliably produces a specific class of failure.
A condition under which a previously documented rule produces incorrect results because it was applied outside its domain.
If you are unsure whether your discovery qualifies: err on the side of submitting. Low-confidence suggestions are marked as such. The review process handles filtering.
Download the Contribution Guide below and upload it at the start of your next AI-assisted development session. When your session ends, send the message: “Run NACODEX closing routine.”
Your AI assistant will conduct a structured interview (4–6 questions) and produce a formatted file: NACODEX_Update_Suggestion.md. The guide contains the full interview structure, output template, and an instruction block addressed directly to the AI assistant.
Attach NACODEX_Update_Suggestion.md and send to nacodex@pukapasoft.xyz. Subject line format: NACODEX Suggestion — [Type] — [One-line description]
Suggestions are reviewed against the current public edition. Integrated suggestions are acknowledged by reply with a reference to the update number. There is no timeline commitment for review — this is a small-scale, best-effort project.
NACODEX is a non-profit, independently maintained effort. If it saved you debugging time and you’d like to contribute something back, a Solana donation is welcome. No pressure, no pitch — just an address for those who want one.
CppVkC7ePwof1M2jC2Z5NRxuBPD2jY8pn4UykP5Vi72y
click address to copy