Onboard Quick, Onboard Right

Getting into a project fast is only half the job — getting in right is the other half. Why onboarding shouldn't rely on luck.

When we think about software development, we often think about adding or changing of code and the understanding of the code before that. Setting up the development environment is often overlook until someone on-broard the project who are not familiar with the toolchain. The setup can range from just having the compiler to multiple setup steps and be a simple sentense like "Use python" to super complext make file. Even with an automate installtion script, it is not always up to date and/or work with different environment. There also "multiple project" interferances like version conflict or worst "almost" conflict. Depending on how that goes, you usually fall into one of three buckets:

  • Really Lucky: Everything works perfectly, and you never have to think about your environment again.
  • A Bit Lucky: The setup fails immediately. You hit a missing dependency or a hard version conflict right away. It is frustrating, but you are forced to resolve the roadblock before you can start working. Fixing the problem might not always success so you can't proceed OR it can bring you to the next category "Unlucky".
  • Unlucky: The setup appears to work so the development proceed while discrepancies are still there waiting to cause problem.
The hidden costs of development environments: the Really Lucky, A Bit Lucky, and Unlucky onboarding paths, and the slow creeping tax of invisible discrepancies that the Unlucky path hides
Three ways onboarding goes — and the creeping tax the “good enough” path hides.

For many developers, that “good enough” setup in the third bucket is where the process ends, allowing them to push forward without much thought. But the real cost of environment management isn't an upfront roadblock — it's the slow, creeping tax of invisible discrepancies.

A minor version difference in a compiler, a globally installed package that silently alters a project's behavior, or a toolchain update that inexplicably breaks an older codebase months later. These issues don't stop you on day one; they surface as mysterious bugs during code review or broken builds in CI. Too often, we just accept this fragile state and the inevitable “works on my machine” conversations as a frustrating fact of life. CodingBooth was built because keeping your environment reproducible and isolated shouldn't rely on luck or constant vigilance.

Onboard Quick

Quick onboarding means the distance between “I have the repo” and “I'm writing code” is a single command. No README full of platform-specific install steps, no afternoon spent chasing a missing system library, no Slack thread asking which version of the toolchain everyone else is on. You clone, you run the booth, and the environment builds itself.

git clone <repo> && cd <repo> && ./booth

The environment is declared in the repo, in a small .booth/ folder, so the very first command a new teammate runs is also the command that gives them the exact setup. There's nothing to install by hand and nothing to get subtly wrong. A new hire, a teammate switching branches, or future-you reviving a dormant project all reach a working environment in minutes — not on day three.

Onboard Right

Quick is only half of it. The “Unlucky” path from the start of this post is quick too — it just happens to be wrong in ways you won't notice for weeks. Onboarding right means everyone gets the same image: the same compiler at the same version, the same libraries, the same running services, every time the booth is built.

Because the booth is created from its definition and thrown away when you're done, there's no long-lived machine accumulating drift. The definition lives in version control next to your code, so the environment changes only when the repo changes — reviewed in the same pull request as everything else. That's what turns off the creeping tax: no minor version difference sneaking in, no globally installed package silently changing behavior, no “it used to build.” The environment stops being a variable, so the only thing left to explain a bug is the code itself.

CodingBooth: Quick and Right OnBoarding with One Command

Quick onboarding gets people working today; right onboarding keeps them and everyone after them working without paying the creeping tax. You don't have relia on luck to get both. Declare the environment once in the repo, and every clone after that is both fast and correct by construction.

See It in Action

The idea is only convincing if you can run it. Each of these is a ready-made booth you can fetch and launch in two commands — booth example try to grab it, then one command to run. Every line of output below is real, captured from an actual run; browse the full set with booth example list.

./booth example try fsharp ./fsharp-demo

Try a language without committing — fsharp

The friction in evaluating a language is the install: the .NET SDK is a heavy thing to put on your machine for a weekend's curiosity. In a booth it's one command, nothing on your host, and the .NET version is pinned — so the playground behaves the same for whoever you send it to. That's quick (no install) and right (reproducible) at the same time.

$ ./booth --silence-build -- ./run-fsharp.sh      # Run `./run-fsharp.sh` inside booth.

== customers.csv ==
customer_id,age,annual_income_k,spending_score
1,25,25,18
2,28,22,15
3,23,30,22
...            # More data
29,38,57,51
30,33,51,46

=== Customer analytics (F# in CodingBooth) ===
Loaded 30 customers from customers.csv

Summary statistics:
  age               mean=  33.8  min=  22.0  max=  48.0  std=  6.6
  income (k$)       mean=  55.2  min=  21.0  max=  92.0  std= 24.8
  spending (1-100)  mean=  50.3  min=  12.0  max=  90.0  std= 26.5

K-means clustering (k=3) on income vs spending:
  Segment 1: 10 customers | centroid income= 25.5k spending=18.5
             customers: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  Segment 2: 10 customers | centroid income= 54.6k spending=49.6
             customers: 21, 22, 23, 24, 25, 26, 27, 28, 29, 30
  Segment 3: 10 customers | centroid income= 85.6k spending=82.7
             customers: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

You can play around with the code, make changes and rerun, all these without having to install f-sharp (dotnet).

Skip the dependency hell — playwright

Playwright's pain is environmental: browser binaries, a pile of system libraries, and a browser build that must match the Playwright version or your tests flake in CI. The booth pre-bakes the browser pinned to the project's version, so the documented command just runs — and it drives a real browser against real pages, not a mock:

$ ./booth -- ./run-screenshot.sh
Visiting https://news.ycombinator.com/ ...
Page title: Hacker News
Screenshot saved to screenshot.png
The Screenshot of Hacker News
The Screenshot of Hacker News.

The system-library dance, done once — systemlib

The hard part of C and C++ isn't the compiler — it's the libraries. You need the right libxxx-dev packages so the headers and the shared objects are present, you need the build to find and link them, and you need everyone on the same versions or you get an "undefined reference" on one machine and a subtly different bug on another. A booth bakes the libraries in, pinned, so the build just resolves them:

$ ./booth -- ./run-linkcheck.sh
-- Found CURL: /usr/lib/x86_64-linux-gnu/libcurl.so (found version "8.5.0")
-- Found SQLite3: /usr/lib/x86_64-linux-gnu/libsqlite3.so (found version "3.45.1")
[100%] Built target linkcheck

URL                                              RESULT STATUS
------------------------------------------------ ------ ------
https://www.iana.org/                            ALIVE  200
https://www.google.com/                          ALIVE  200
https://www.google.com/this-page-does-not-exist  DEAD   DEAD: HTTP 404
https://this-host-does-not-exist-9x8y7z.invalid  DEAD   DEAD: Couldn't resolve host name

Summary: 2 alive, 2 dead. Recorded 4 rows to linkcheck.db

The program is a link checker: it reads a list of URLs, checks each one with libcurl, and records every result — URL, timestamp, status — into a SQLite database. Two real system libraries, installed with apt and linked by CMake's find_package — exactly the part header-only libraries let you skip. Nothing lands on your host, and the pinned APT_SNAPSHOT means the same library versions build for everyone, every time.

A whole stack on your laptop, then gone — kind-app

The hardest onboarding is the realistic one. This booth bakes Docker-in-Docker, KinD, kubectl, Go, Bun and Python — all pinned — and stands up a real React + Go + PostgreSQL app on an in-container Kubernetes cluster from one command chain:

$ ./booth -- './start-cluster.sh && ./build.sh && ./deploy-app.sh && ./status.sh'
=== Deployment complete ===
NAME                 READY   STATUS    RESTARTS   AGE
api-...              1/1     Running   0          8s
export-service-...   1/1     Running   0          9s
postgres-...         1/1     Running   0          22s
web-...              1/1     Running   0          8s

✔ SUCCESS: Cluster 'kind' is UP
kind-control-plane   Ready   control-plane   87s   v1.32.2

The cluster lives inside the booth, so tearing it down is just stopping the booth — your host is left exactly as clean as it started.

Batteries included, GUIs and all — data

A data project is a pile of separate installs that all have to agree: a database, a SQL client, Python with the right libraries, a notebook server. This booth bakes the whole workbench — PostgreSQL, DBeaver, JupyterLab, Python with matplotlib and psycopg2, and a small web dashboard — and wires every one of them to the same seeded dataset. It's the kind-app idea made graphical: instead of a cluster on the command line, one command opens a full desktop in your browser with the tools already running.

$ ./booth        # opens an XFCE desktop in your browser
An XFCE desktop running in the browser, with DBeaver, JupyterLab, Visual Studio Code, Firefox and Chrome all sitting on the CodingBooth wallpaper
One command, a whole desktop of tools — DBeaver, JupyterLab and VS Code, all in the browser.

DBeaver opens with its connection to the demo database already configured; JupyterLab is up with a notebook that queries the data and charts it with matplotlib; and a Sales Explorer dashboard is serving live rows straight from PostgreSQL. One dataset, seen through every lens at once:

The Sales Explorer web dashboard: filters, summary stat cards, and two bar charts of revenue and quantity by category, all drawn from the seeded PostgreSQL data
The Sales Explorer dashboard — real rows from the booth's PostgreSQL, charted in the browser.

Stop the booth and the whole workbench — database, notebook, dashboard, every GUI — is gone, with nothing left installed on your host.

Installing CodingBooth is one command:

curl -fsSL https://codingbooth.io/install.sh | bash

Happy coding!
Nawa Man


Learn More

Comments