blog · Artikel auf Englisch
Does the architecture fit? An architecture review of Snotra
A code review asks whether the code is right. An architecture review asks whether the structure fits what the app has to achieve. How I did it for Snotra: an interview for the quality goals first, then 29 scenarios checked against the code, the decisions written down, and 38 issues.
Over the last weeks Snotra went through a code review in eighteen blocks: 118 findings, block by block, all asking the same question — is this code correct and clean? What that review never asked was whether the blocks themselves were cut in the right places. It took the architecture as given, because it used the architecture as its grid.
So the next step was the question one level up: does the architecture Snotra has fit what Snotra has to achieve? That question has a catch. You can’t answer it from the code alone, because “fit” needs something to fit to. Without written quality goals, every judgement about an architecture is a matter of taste.
Two phases, strictly in this order
The issue behind the review split it into two phases, and phase 2 was not allowed to start before phase 1 was agreed.
- Elicit the quality requirements. A structured interview, not a document drafted in advance and handed to me for approval. arc42 chapters 1, 2 and 10 served as orientation: context and constraints, quality goals, quality scenarios. The result is
quality-requirements.md, written on October 15 and agreed the day after. - Evaluate the architecture against them. ATAM-light: each scenario was mapped to the architectural approaches meant to serve it — layering and ports, the composition root, IPC contracts, the process model, the permission system, storage, self-update, the renderer — and rated supported, partly supported, at risk or not built, each time with the evidence in the code. The result is
architecture-review.md.
A full ATAM brings the stakeholders together in workshops. For a solo project that is oversized: the only person at the table is me, so the interview replaces the workshop. The evaluation itself ran in four strands in parallel — security and privacy; robust operation; the separable core and the documentation; usability, parity and performance — each reading the source, the docs, the manual and the CI history. The claims the high findings rest on were checked again by hand.
The report also names its limits, and I think that belongs in a review: nothing was executed, one census of error handling is a heuristic, and not a single performance number in it is measured. The last one became a finding of its own.
What the interview asked
The interview started with the people the system is for. Four groups came out of it: public users, who download the app and have to get along without me; me, keeping a large code base changeable alone; the AI agents that write much of the code and have to find their way through the documentation; and future consumers of the core — a headless mode, a CLI, a mobile app. Then came the questions whose answers shape an architecture:
| Question | Answer |
|---|---|
| Who is Snotra built for, first of all? | Public users |
| Which platforms rank equally? | All three: macOS, Windows, Linux |
| Which providers must work? | Every provider the app offers |
| How serious is separating back end and front end? | A concrete goal: headless, CLI, mobile |
| A feature can’t reach every platform in time — what then? | Ship it where it works, show the gap, track it in an issue |
| Update check: security or privacy? | Automatic by default, but switchable |
| Faster in main or the renderer, or cleaner through the core? | Always through the core |
| May a feature send data to a service the user did not set up? | Only after consent |
| Is performance a quality goal? | Yes, with budgets |
| Remote control: how far may it reach? | Localhost only |
| Which failure is the worst? | Silent misbehaviour |
| What is Snotra not? | A multi-user server, or an IDE |
The most important answer was the ranking, because it decides what wins when two goals collide:
- Agent security — the agent can’t do anything the user did not allow.
- Usability and accessibility — a public user gets along without help, WCAG 2.1 AA in light and dark.
- Privacy — data stays on the machine and with the services the user set up.
- Robust operation — updates, broken files and failed runs never cost data, and nothing goes wrong silently.
- Separable core — the application core runs without Electron.
Below these five, and still binding: safe changeability, portability and parity, a bilingual interface, and performance. Each goal was then made checkable in scenarios of the form stimulus → response → measure — 29 of them. One example, SEC-3: a safety mechanism can’t be established, say the OS keyring is missing. The app falls back to the stricter state and says so. The measure: no path that fails open, every degraded state visible. A scenario like that can be checked against the code; “the app should be secure” cannot.
The architecture at a glance
What was evaluated, roughly: an Electron app with a sandboxed renderer, a main process that holds every secret and decides every access to the outside, and inside main, layers whose dependencies point inwards to a core without Electron. The tags mark where the review found the largest risks.
architecture.md.What came out
Of the 29 scenarios, two are supported, fifteen partly supported, ten at risk, and two are not built yet. Per goal:
| Goal | Verdict | In short |
|---|---|---|
| 1 · Agent security | partly supported | A mature design, with two paths that fail open |
| 2 · Usability | partly supported | Accessibility checked by hand on macOS only |
| 3 · Privacy | partly supported | No telemetry; the update check couldn’t be switched off |
| 4 · Robust operation | at risk | No safety net against silent misbehaviour |
| 5 · Separable core | at risk | The core holds about 5.3k of 34k lines of backend logic |
| Safe changeability | partly supported | A real gate on three platforms, but too many red runs |
| Portability and parity | at risk | Gaps between providers and on Windows |
| Bilingual interface | partly supported | A third language would need code changes |
| Performance | at risk | Nothing was measured |
The finding that hurt most is goal 4. The interview had named silent misbehaviour the worst failure — and there was no safety net for it: no global error handlers, no log file, and a chat that could not be saved was lost on quit without a word. The other surprise was goal 5. The import guard holds, so the core really knows nothing of Electron. But it is small, and the renderer, not the core, owns the conversation. A CLI built today would have to take most of the main process along.
The decisions
An architecture review is also a good moment to write down decisions that so far only existed in the code or in my head. Three kinds came out of this one.
Rules for conflicts, from the interview. These override the ranking where they apply:
- Security beats usability. A softer setting may be offered per workspace, but it is never the default.
- Privacy has a floor. No data goes to a service the user did not set up before they agreed — with the service named and the consent revocable.
- Parity: ship, and show the gap. A feature that can’t reach every platform or provider in time ships where it works; the gap is visible in the UI, named in the manual and tracked in an issue.
- Logic always goes through the core, even when building it in main or the renderer would be faster. The ranking decides what the product does; this rule decides where the logic lives.
- Remote control stays on the machine. Headless and mobile front ends reach the core over localhost only.
- Prompt-injection detection is an extra layer, never the protection. Isolation and approvals protect; a detector may only warn.
The founding decisions, questioned for the first time. The code review had treated them as given. Here they were open:
- Electron stays. It costs memory and a self-built updater, but it serves parity across platforms and offline operation, and it does not block the separable core: only four files in the main process import it. The rule to record: Electron is the shell, the core runs on plain Node.
- Plain JavaScript stays — no build step — but the core, the ports and the contracts get type checking through
tsc --checkJs, because drifting contracts showed up in five blocks of the code review. - A renderer framework is still open. About twenty findings of the code review — redraw, focus, races — are the cost of going without one. But a framework wouldn’t fix who owns the conversation, so that move comes first, and the framework question gets its own issue afterwards.
The order of the work. Severity first, then the rank of the goal, cheap fixes first where the risk is equal: the two small high findings, then a safety net against silent misbehaviour, then measuring before optimising, then the separable core in a fixed sequence — checked ports, an Electron-free composition, confirmations behind a port, the conversation into the core, the rest of the logic after it.
38 issues
As with the code review, every finding became an issue of its own, AR-01 to AR-38, each linked to the review issue and with its own definition of done: 10 high, 25 medium, 3 low. Two further findings were already covered by existing issues and were not filed again. Fixing them is a separate step, prioritised like any other work — the review only says what and in which order.
That separate step started right away. A day after the report, 13 of the 38 are closed, among them both of the small high fixes, the safety net against silent misbehaviour, a running turn that is now saved after every finished step instead of only at the end, a switch for the update check, and a harness that measures the performance budgets instead of guessing them. Twenty-five are open, the separable core among them, which is the longest stretch of the road.
The review also says when to look again: before the first second front end is started, once the performance numbers are measured — and otherwise in six months, or as soon as a quality goal changes.
If you want to look closer
Everything is in the repository: the quality model with the full interview record, the review with every scenario, trade-off and sensitivity point, and the issue that links all 38 findings. If you maintain a project of your own, I’d be curious whether you write your quality goals down before you judge your architecture, or only afterwards — the Discussions are open for it.