AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

A shared core does not own the native interface

Ghostty's 2025 GTK rewrite shows the cost of avoiding a toolkit's object model. Keep shared core logic while making native object ownership explicit.

Citiți articolul în română

Sharing application logic across platforms does not remove each interface's ownership rules. If a toolkit organizes objects through references, signals and disposal, a separate lifetime system still has to meet those rules somewhere. Ghostty's GTK rewrite offers a reason to adopt that boundary deliberately rather than maintain an unofficial parallel object model around it.

This November 2026 retrospective looks back at Mitchell Hashimoto's 14 August 2025 account. He described replacing the procedural GTK approach with fuller use of GObject from Zig. His explanation is a maintainer's account of that project, not independent proof that every GTK application should be rewritten or that Zig determines the best GUI architecture.

The configuration has more than one reader

Hashimoto's central example was reloadable configuration. He described wrapping the Zig configuration in a reference-counted GObject so consumers could retain the old value while change notifications propagated. Previously, coordinating the old configuration's release across GUI consumers was a source of complexity. The lesson is about agreeing on ownership at the interface, rather than changing the language of the shared core.

The GObject concepts documentation, consulted in November 2026, describes reference counting, object properties and signals as parts of its object system. Adopting them gives an application conventions that its toolkit understands. A wrapper that stores a pointer while ignoring those conventions still needs to explain which side may destroy the pointee and when callbacks stop using it.

A reload makes the problem easier to see because the user thinks of one action while several interface objects may observe it. Replacing a value does not establish that every observer has finished with the old one. The review should ask which objects can retain the old configuration and what releases their claims. Counting updates delivered is a different question from proving that no reader remains.

That reasoning applies even when all relevant code is in one language. A language choice does not, by itself, define the ownership contract of an external API. Conversely, using a foreign object model does not require abandoning a shared domain model. The architectural job is to make the translation between them small enough to understand.

An abstraction can duplicate the work it hides

There is a legitimate motive for avoiding toolkit-specific types in application logic. A shared core should not become dependent on the details of a menu or a window. That separation helps keep reusable behavior distinct from the desktop interface that presents it.

The problem begins when the interface layer tries to avoid the toolkit's model as well. It can end up recreating notifications, object relationships and destruction ordering in its own conventions. The resulting code still depends on the toolkit, but a maintainer now has to understand two systems and the coordination between them. A clean-looking core boundary can conceal a complicated local boundary immediately outside it.

This is an interpretation of the rewrite, not a measurement of how much code any team would save. Adopting native conventions can also introduce work: wrappers, bindings and integration-specific knowledge still need maintenance. The reasonable goal is one explicit ownership arrangement for each boundary, rather than a promise that toolkit adoption eliminates lifetime reasoning.

For an illustrative design review, follow one object from creation through its last callback. Identify where a reference is retained, how the callback learns that its owner is gone and which operation ends its access. If the explanation relies on two components always being destroyed together without a stated contract, the abstraction needs more scrutiny. No such review of an AL+ application is reported here.

Shared code cannot certify the platform shell

In the August account, Ghostty combined a shared Zig core with separate platform interfaces: Swift on macOS and GTK on Linux and BSD. That historical arrangement demonstrates that sharing a core and accepting native interface responsibilities are compatible choices. It does not make the platform shells interchangeable or establish their current implementation in November 2026.

Hashimoto said the rewritten GTK application was on the main source branch and intended for the upcoming 1.2 release. That was a source-state and release-intent statement on 14 August, not proof that all users had already received it. This article does not audit the present Ghostty tree or certify a later binary's memory behavior.

The same restraint applies to his reported Valgrind work. It supports his description of how the team investigated that rewrite. It cannot become a guarantee that a language, a wrapper or a different application is free of memory faults. Diagnostic tools provide evidence under the conditions exercised; ownership still needs an intelligible contract.

A stable adapter may deserve to stay

The strongest counterargument is an existing interface adapter whose ownership is clear and whose behavior is well supported. Replacing it merely to resemble the toolkit more closely can add churn without solving a demonstrated problem. A rewrite should have a concrete reason beyond aesthetic discomfort with two programming styles.

That argument weakens when recurring failures occur exactly where the adapter translates lifetimes or imitates toolkit features. In that situation, compare the work of maintaining the parallel model with the work of adopting the native one. Keep the shared calculation or domain behavior separate while allowing the interface layer to use the conventions of the interface it owns.

For custom software development, ask for an ownership explanation at handover, alongside the platform support promise. Who owns configuration while the interface updates, and what ends that ownership? Ghostty's historical example makes that question useful. A shared core earns reuse; a native interface still needs someone to understand when its objects live and when they stop.