DEV Community

Cover image for Perfect Elixir: Onboarding
Jon Lauridsen
Jon Lauridsen

Posted on

Perfect Elixir: Onboarding

Onboarding into a project is an often overlooked aspect of team productivity, where the time spent configuring tools, dependencies, and local environments can represent a significant hidden cost - both in initial setup and ongoing maintenance. And beyond the immediate time investment, how we handle environment setup often reflects deeper values on the kind of quality and care a team wishes to provide.

This article explores streamlining developer onboarding via semi-automated scripting, to deliver a simple, balanced onboarding process without getting bogged down in high technical maintenance.

ℹ️ BTW I've worked on projects where onboarding could take over two weeks 😱. A truly frustrating waste of time, involving senior developers debugging new developer's machines for hours to resolve various errors — a waste that repeated with every new team member. We can and must do better!

Table of Contents

 

Redefining Onboarding

By "onboarding" we mean someone who has completed the basic organizational introduction — like setting up email and user accounts — and now is looking to become a productive contributor to our project.

Often, this relies on a detailed developer guide, with more or less detailed steps a developer must follow. But guides can have several drawbacks:

  1. Lack of Testability: Text-based guides can't be automatically tested, increasing the likelihood of outdated or incorrect instructions.
  2. Accumulated Complexity: Because guides offer no way of automation, every new onboarding requirement results in additional steps. This makes guides increasingly complex, and resistant to simplification.
  3. Maintenance Burden: Keeping guides up-to-date requires manual effort, often done under time pressure when a new developer hits a problem and needs urgent help to resolve it.

That's not to say guides are disastrous, but merely to lay out the argument that guides do have drawbacks that are worth considering. What practical solutions might we imagine that could address those drawbacks, without itself ending up so technically sophisticated that it would suffer from its own maintenance issues?

Let's anchor around the most ambitious user experience we can imagine: The most aggressively simple onboarding I can imagine is one where we ask a new developer to execute a single command — without requiring any prior dependencies to be installed. That is extremely simple, and such a script could leverage the benefits of test automation to lend itself to refactoring and maintainability.

On macOS and Linux systems, the easiest way to run a remote script is:

$ curl -Ssf https://…/script | sh
Enter fullscreen mode Exit fullscreen mode

Let's see what kind of onboarding can come from that approach!


However, there's an immediate technical constraint: In order to onboard we'll need to inspect the developer's current shell environment to determine what needs to be done, and that is not possible when piping to sh as it starts a new shell. Instead, we need to source the script directly:

$ curl -fsSL https://…/script > /tmp/script && source /tmp/script
Enter fullscreen mode Exit fullscreen mode

This still remains radically simple, so let's keep exploring where this idea takes us. Next, we must consider a key requirement: How we install global system tools on a user's machine?

 

Beware Global System Changes

There's no getting around our onboarding requires installing pkgx (as outlined in the earlier environment setup article). But there are pitfalls to be aware of for how one makes a user install such system-wide tools, with common mistakes being too invasive and attempting to fully automate everything.

Why are those mistakes? Because developer setups are varied, and from hard-earned experience I can say most system-assumptions turn out to be wrong or end up causing annoyances. We need to find the fewest, least invasive steps necessary to get started with our project, and then we only insist on zero variance within our project. We have to avoid impacting the user's machine outside of working with our project, for several reasons:

  1. It's Invasive: Many developers customize their environments, and tools that cause global changes can ruin their other projects. This is bigger than just the team's developers; one day you might want an external developer to jump in and lend their expertise and they'll have zero patience for being forced through too many hoops. We definitely cannot make assumptions about the user's home-directory files, their configuration, or the placement of our repository.
  2. It's Brittle: Making any non-trivial code work across varied systems invites code errors wherever we make wrong assumptions. Too much clever code is a frequent cause of errors, and a new developer's first experience should not be with a script that fails in various ways.
  3. It's Hard to Maintain: For any change that we insist must be made, there'll be someone who wants to do it differently. If the annoyance is high enough, then onboarding ends up supporting multiple setup permutations, and they get very complicated and tiresome to maintain very quickly.

Luckily, our only required core system tool is pkgx, so we're already in a good position for making a simple onboarding experience. But the question remains: How do we make a developer install pkgx?

The simplest answer is: We ask them to. pkgx already offers a very streamlined installation process (it doesn't even require sudo!), so lets just leverage that instead of building our own bespoke installation code. It causes our onboarding to become less about installing, and all about guiding the user to install what we need of them. By simply asking, we are automatically respecting the developer's system, andwe massively reduce the risk of us writing erroneous code. But we'll still do this via a script, so we can leverage test automation to increase maintainability.

Let's start implementing this balanced approach.

 

Implementing the Onboarding Script

Building Trust

What if our first step is to gain the user's trust? We are, after all, asking them to run a script on their machine, so lets be transparent about what our script will do.

We begin by informing the user that our script will only inspect their environment and make suggestions, never make any actual changes:

$ URL="https://raw.githubusercontent.com/gaggle/perfect-elixir/refs/heads/perfect-elixir-5-onboarding/bin/bootstrap"

$ curl -fsSL $URL > /tmp/boot && source /tmp/boot
🚀 Welcome to the Perfect Elixir Onboarding Script! 🚀

This script will help you set up your development environment for our project. 

🔒 Trust and Transparency: 
  - This script NEVER makes changes to your system
  - It ONLY inspects your environment and makes suggestions

Ready to proceed? [y/N]:
Enter fullscreen mode Exit fullscreen mode

🖥️ Terminal

Bootstrap script introducing itself and prompting the user to proceed

This approach achieves several goals:

  1. Clarity: Clearly states the script's purpose.
  2. Transparency: Emphasizes that no changes will be made, period.
  3. Consent: Allows the user to opt-in before any actions are taken.

By being upfront about this, we hopefully make users comfortable to run our script and we show we'll respect their systems while assisting in getting onboarded.

ℹ️ BTW I won't be showing the underlying bootstrap code because it's simple scripting that just outputs text. We'll add more steps in the next sections, and the full bootstrap script is available here if you do want to see the code.

Step 1: Install pkgx

First, lets check if pkgx is installed:

Ok to proceed? [y/N]: y
• Checking for pkgx… x

📦 pkgx is not installed

User action required: Install pkgx
──────────────────────────────────
pkgx is our package manager for handling system dependencies. Learn more at https://pkgx.sh.

Here are quick ways to install it:
1. Via Homebrew:
   $ brew install pkgxdev/made/pkgx
2. Via cURL:
   $ curl -Ssf https://pkgx.sh | sh
Other installation methods at https://docs.pkgx.sh/run-anywhere/terminals.

ℹ️ pkgx installation is simple, and installing via Homebrew does not require sudo.

After pkgx has been installed please source this script again.
Enter fullscreen mode Exit fullscreen mode

Step 2: Activate Shell Integration

Next, we need to ensure pkgx's shell integration is active:

Ok to proceed? [y/N]: y
• Checking for pkgx… ✓
• Checking pkgx shell integration… x

🔧 pkgx shell integration is not active

User action required: Activate pkgx shell integration
─────────────────────────────────────────────────────
To activate pkgx shell integration, run the following command:

  $ eval "$(pkgx --shellcode)"

Integration writes one line to your .shellrc, and also activates integration in your current shell session.

ℹ️ For more information about shell integration see: https://docs.pkgx.sh/using-pkgx/shell-integration.

After shell integration has been activated please source this script again (there is no need to restart your terminal).
Enter fullscreen mode Exit fullscreen mode

Activating shell integration unlocks pkgx's dev command, essential for managing project dependencies.

Step 3: Clone the Repository

Now we're ready to check if our repository has been cloned:

Ok to proceed? [y/n]: y
• Checking for pkgx… ✓
• Checking pkgx shell integration… ✓
• Checking repository is cloned… x

🌿 Repository not found

User action required: Clone the repository
──────────────────────────────────────────
Please clone the repository using your preferred method:

1. Via GitHub CLI:
   $ pkgx gh repo clone gaggle/perfect-elixir
2. Via SSH:
   $ git clone git@github.com:gaggle/perfect-elixir.git
3. Via HTTPS:
   $ git clone https://github.com/gaggle/perfect-elixir.git

After cloning, navigate to the project directory and source this script again.
Enter fullscreen mode Exit fullscreen mode

Note how the first suggestion leverages pkgx's ability to run arbitrary tools, in this case by ephemerally running GitHub's CLI tool.

Step 4: Activate the Development Environment

Finally, we ensure the pkgx development environment has been activated for our repository:

Ok to proceed? [y/n]: y
• Checking for pkgx… ✓
• Checking pkgx shell integration… ✓
• Checking repository is cloned… ✓
• Checking development environment is active… x

🔧 Development environment is not active

User action required: Activate the development environment
──────────────────────────────────────────────────────────
To activate the development environment, run:

  $ dev

This will make project-specific versions of dependencies available within the repository.

After activating, please source this script again.
Enter fullscreen mode Exit fullscreen mode

Onboarding complete!

When all checks pass, the script confirms completion and guides the user to start using our development workflows:

Ok to proceed? [y/n]: y
• Checking for pkgx… ✓
• Checking pkgx shell integration… ✓
• Checking repository is cloned… ✓
• Checking development environment is active… ✓

✅ Onboarding complete!

- To start working on the project, run:

    $ bin/doctor
Enter fullscreen mode Exit fullscreen mode

🖥️ Terminal
Here is the full flow from a factory-reset machine to being ready to work on the project:

Running bootstrap script on a fresh machine, pasting in each of the suggested commands until onboarding is complete

This clear, step-by-step approach ensures the developer can get fully set up in a few minutes.

ℹ️ BTW as soon as the first pkgx step is completed, the entire pkgx ecosystem is available. That means subsequent steps can easily leverage powerful tools such as the GitHub CLI or even entire programming languages, which is very powerful.

 

Test Automation

Let's explore how we can automate testing of our onboarding process across different environments and shells.

ℹ️ BTW test automation is a rich topic we'll explore more deeply in a future article. For now, let's keep it to an effective example that can demonstrate the core benefits of test automation.

Here is a script that sources the onboarding guide, asserts the step is as expected, and then implements that step. It goes through all steps until onboarding is complete:

curl -fsSL $BOOTSTRAP_URL > /tmp/bootstrap && source_bootstrap
assert_output "User action required: Activate pkgx shell integration"
do_step "Integrating pkgx" "eval \"\$(pkgx integrate)\""

source_bootstrap
assert_output "User action required: Clone the repository"
do_step "Cloning the repo" \
  "pkgx gh repo clone gaggle/perfect-elixir" \
  "cd perfect-elixir" \
  "git checkout perfect-elixir-5-onboarding"

source_bootstrap
assert_output "User action required: Activate developer environment"
do_step "Running dev" "dev"

source_bootstrap
assert_output "Onboarding complete!" || exit 1
Enter fullscreen mode Exit fullscreen mode

When we run this in our CI pipeline, it verifies each step successfully completes:

SOURCING BOOTSTRAP
Output contains: "User action required: Activate pkgx shell integration"… ✔
Step: Integrating pkgx… ✔

SOURCING BOOTSTRAP
Output contains: "User action required: Clone the repository"… ✔
Step: Cloning the repo… ✔

SOURCING BOOTSTRAP
Output contains: "User action required: Activate developer environment"… ✔
Step: Running dev… ✔

SOURCING BOOTSTRAP
Output contains: "Onboarding complete!"… ✔
Enter fullscreen mode Exit fullscreen mode

And by running these tests in CI, we can verify onboarding works across many different operating systems and shell configurations by running them in a test matrix, like this:

Screenshot from GitHub Actions showing a matrix of E2E tests running against the combinations of Bash and Zsh shells, and MacOS 15 and 14

ℹ️ BTW these E2E tests already caught a real bug where Bash and Zsh subtly differed! Test automation is the best ❤️. The full test file can be seen here.

 

Conclusion

We set out to reimagine onboarding, and ended up with something remarkably simple: A single command that guides developers through onboarding. The entire onboarding instruction becomes just:

Run this and follow the steps: $ curl -fsSL https://…/script > /tmp/script && source /tmp/script

Of course, real-world implementations need to consider where to host the script securely. We had it easy here because the repository itself is public, but for an organization perhaps a simple static web server behind a VPN could be ideal, or even a shared folder could work for smaller teams. The key is maintaining simplicity while ensuring security.

ℹ️ BTW When implementing script-based onboarding, consider security practices:

  • Host scripts on trusted infrastructure
  • Use checksums for verification
  • Inspect scripts before running them

But the true power of our approach is its flexibility: The script simply detects and guides, which makes it simple to extend for specific needs such as checking for repository access, or guiding the developer through ticketing systems, etc. And with pkgx available, the possibilities for helper tools are unlimited - though we should maintain our principle of never making direct system changes.

We've shown how stepping back and orienting towards core principles can lead to simple, maintainable solutions. This minimal investment in scripting over documentation promises a smoother onboarding, happier developers, and is a clear commitment to a team that cares to always create great experiences.

Top comments (0)