I Built the Universal Run Button for Every Project on My Machine

M

Matvei

Guest
Developers spend a surprising amount of time dealing with project setup.

Before writing a single line of code, you often need to determine which runtime a project uses, install dependencies, navigate documentation, and remember language-specific commands. Switching between Node.js, Python, and Rust projects can feel like context-switching between entirely different ecosystems.

I wanted to explore a simple question:

What if running a project was always the same command, regardless of the language behind it?

That question led me to build Axiom, a Rust-based command-line tool designed to detect, prepare, and run projects automatically.

This article focuses on the technical decisions, architecture, challenges, and lessons learned while building the project.

The Problem​


Most developers have experienced something like this:

Node.js​


Code:
npm install
npm run dev

Python​


Code:
pip install -r requirements.txt
python main.py

Rust​


Code:
cargo run

Each ecosystem has its own package manager, conventions, and startup process.

Individually, these systems work well. The friction appears when you frequently move between projects and languages.

I wanted a workflow that looked more like:

Code:
axiom run

No matter what type of project was inside the directory.

Why Rust?​


I chose Rust for several reasons.

Performance​


Project detection and orchestration are primarily filesystem operations. Rust's performance makes these tasks extremely fast while keeping resource usage low.

Cross-Platform Distribution​


One of the goals was making installation easy.

Rust allows static binaries that can be distributed without requiring users to install additional runtimes.

Reliability​


Axiom spends most of its time examining project structures and executing external commands.

Rust's type system helped catch many mistakes during development before they could become runtime bugs.

Architecture Overview​


At a high level, Axiom consists of four major systems:

  1. Project Detection
  2. Dependency Graph Construction
  3. Provider Abstraction
  4. Execution Orchestration

Detection Layer​


The first challenge is determining what kind of project exists inside a directory.

Axiom scans for common indicators:

LanguageDetection Files
Node.jspackage.json
Pythonrequirements.txt, pyproject.toml
RustCargo.toml

A simplified version of the logic looks like:

Code:
Directory
    ↓
Find Signature Files
    ↓
Determine Project Type
    ↓
Select Provider

This allows the rest of the application to remain language-agnostic.

Provider Abstraction​


One of the most important design decisions was separating project detection from project execution.

Instead of writing special-case logic everywhere, Axiom uses providers.

Conceptually:

Code:
Provider
├── Node Provider
├── Python Provider
└── Rust Provider

Each provider implements behavior such as:

  • Installing dependencies
  • Preparing environments
  • Running projects
  • Reporting status

The orchestration layer doesn't need to know whether it's talking to Node, Python, or Rust.

It simply communicates with a provider interface.

This made adding new ecosystems significantly easier than modifying a large collection of conditional statements.

Dependency Graphs​


As the project grew, I wanted a structured way to represent relationships between tasks.

Rather than treating setup as a linear list of commands, Axiom models operations as a dependency graph.

For example:

Code:
Install Runtime
        ↓
Install Dependencies
        ↓
Prepare Environment
        ↓
Execute Project

Representing execution this way provided a foundation for future improvements such as:

  • Parallel operations
  • Smarter caching
  • Incremental execution
  • Build optimization

Even though the current implementation is relatively simple, designing around graphs early makes future expansion easier.

The Cache System​


Repeated setup operations can become expensive.

To avoid unnecessary work, Axiom maintains a cache directory.

The cache stores metadata that allows the tool to determine whether certain preparation steps have already been completed.

Conceptually:

Code:
~/.axiom/cache/

Benefits include:

  • Reduced startup times
  • Less repeated dependency work
  • Consistent execution behavior

Caching sounds simple in theory, but invalidation quickly becomes difficult.

Determining when cached information is still valid became one of the more challenging parts of development.

Challenges​

Ecosystem Differences​


The biggest obstacle wasn't writing Rust code.

It was handling the differences between language ecosystems.

Node, Python, and Rust each have:

  • Different conventions
  • Different dependency systems
  • Different startup commands
  • Different assumptions

Creating a common abstraction without losing flexibility required multiple redesigns.

Edge Cases​


Simple demo projects worked almost immediately.

Real-world projects exposed issues.

Examples included:

  • Missing dependency files
  • Unexpected directory layouts
  • Alternative package managers
  • Custom startup commands

Many bugs only appeared after testing against actual repositories rather than toy examples.

Scope Control​


A common temptation during development is continuously adding features.

At several points I considered expanding the project into a full development platform.

Instead, I focused on a smaller objective:

Make running projects easier.

Maintaining that focus helped keep development manageable.

Benchmarking​


One question I wanted to answer was whether automation could meaningfully reduce setup time.

The exact results vary by project size and complexity, but testing showed that removing repetitive setup steps can significantly reduce the time required to get a project running.

The most valuable outcome wasn't raw speed.

It was reducing the number of decisions a developer has to make before they can begin working.

What I Learned​


Building Axiom taught me several lessons.

Abstractions Matter More Than Features​


A well-designed architecture creates room for future capabilities.

A poorly designed architecture creates technical debt.

Investing time in provider abstractions and orchestration layers paid off later.

Real Projects Reveal Real Problems​


Many designs seem perfect until tested against actual repositories.

The majority of meaningful improvements came from running Axiom against real-world projects.

Simplicity Wins​


The most useful developer tools often do one thing exceptionally well.

Every feature added to Axiom was evaluated against a simple question:

"Does this reduce friction for the user?"

If the answer was no, it probably didn't belong.

Future Work​


Several areas remain interesting for future exploration:

  • Additional language ecosystems
  • Smarter project detection
  • Enhanced caching strategies
  • AI-assisted project analysis
  • Automated environment diagnostics

The architecture was intentionally designed to allow these capabilities without requiring a complete rewrite.

Final Thoughts​


Axiom started as an attempt to remove repetitive setup work when switching between projects.

What began as a convenience experiment became a deeper exercise in software architecture, abstraction design, and cross-language tooling.

The project reinforced an idea that many developer tools share:

Small reductions in friction compound over time.

When developers spend less energy remembering commands and configuring environments, they can spend more time building software.
 

Thread statistics

Created
Matvei,
Replies
0
Views
3
Back
Top