Native Engineering in TablePro: Zero Shared Code Across Platforms

Background

Modern cross-platform desktop development has largely surrendered to web views and runtime bundling. Most multi-database management clients follow one of two paths. They either bundle a full Chromium runtime and Node.js via Electron, or they pull in an entire Java Virtual Machine to run standard JDBC drivers. The result is familiar to anyone working with large datasets: hundreds of megabytes of baseline memory usage, sluggish startup times, and interface widgets that ignore operating system standards.

TablePro approached this problem from an uncompromising angle. Instead of forcing a unified cross-platform runtime, the project split the platform targets into separate codebases. The macOS and iOS applications live under one directory built entirely with Swift, AppKit, and SwiftUI. The Linux version lives under a separate directory built in Rust with GTK 4 and libadwaita. Between the two implementations, there is exactly zero shared code.

Native database clients that share no code: apple/ holds the macOS and iOS apps (Swift), linux/ the Linux app (Rust, GTK 4).

This dual-engine strategy prioritizes runtime efficiency, human interface integration, and raw execution speed over developer convenience. Both platforms talk directly to native database drivers and operating system networking APIs without intermediate virtual machine layers.

Challenges

The primary architectural challenge with zero shared code is feature divergence. When two distinct engineering stacks implement the same product specification, maintaining protocol parity, query lifecycle handling, and connection state machines becomes difficult.

On Apple platforms, TablePro uses Swift 6 language mode and depends on native system frameworks. Modularity is achieved through a dynamic plugin system. Drivers and heavy external dependencies ship as registry-signed plugins, while advanced analytical features are compiled as optional modules. An inactive module leaves zero residue in memory: no background listeners, no timers, no menu items, and no keychain reads.

On Linux, dynamic plugin ABIs in Rust introduce extreme maintenance overhead. The team resolved this by rejecting plugins entirely for the Linux binary. Instead, drivers link directly into the binary at compile time across strict, unidirectional crates:

# Linux crate architecture
crates/
├── app/        # GTK 4 application, windows, and views
├── core/       # Domain traits, query state, no GUI dependencies
├── drivers/    # Static database engines (Postgres, MySQL, SQLite)
├── net/        # Native TCP and TLS transports
├── session/    # Transaction management and connection pools
├── ssh/        # OpenSSH tunnel integration
└── storage/    # Secret management and file caches

Another major challenge was enforcing strict behavioral and stylistic standards across automated contributions and AI coding agents. The repository maintainers implemented strict verification gates. Beyond standard continuous integration, they deployed custom shell hooks that reject code or documentation containing banned phrases, excessive abstractions, or punctuation splices.

# Enforcing clean checks and repository constraints
./scripts/check-banned-words.sh
cargo test --workspace --all-targets
swift test --package-path apple/TableProCore

Every decision that changes architecture requires an Architecture Decision Record (ADR). For example, Linux ADR 0001 documents the explicit refusal to build a plugin system, while ADR 0015 locks the platform floor to GNOME 50 to guarantee modern libadwaita widget availability.

Results & Lessons

TablePro proves that the conventional wisdom favoring single cross-platform codebases often solves organizational convenience rather than end-user performance. By accepting duplicate business logic across Swift and Rust, the project gained distinct operational advantages:

  • Startup time dropped to near-instantaneous execution because there is no Chromium renderer or JVM bootstrap sequence.
  • Memory footprint remains bounded by actual query result buffers rather than runtime overhead.
  • Platform integration feels native on both sides: macOS users get AppKit split panes and Keychain integration, while Linux users get proper libadwaita styling and GSettings support.

The core lesson for systems architects is clear. Cross-platform frameworks promise speed of development, but they impose a permanent tax on memory, binary size, and platform fidelity. When performance and interface responsiveness are non-negotiable requirements, writing two distinct native applications with dedicated domain crates and zero shared code remains a practical and superior design choice.

Press Cmd K to search برای جستجوی سایت از Cmd+K استفاده کنید