CI-Enabled Developer Onboarding: Welcome Setup-as-code
Every newcomer does setup. Every company needs that setup to work. The question is whether the company treats onboarding as a repeatable capability, or whether it throws the newest developer into the oldest pile of hidden operational debt.
This series is about CI-ing the developer onboarding experience.
Not "write a shell script and hope." Not "put a checklist in the wiki." Not "ask the senior developer which version of Docker still works with the corporate proxy." The point is stronger: developer setup is a domain, and it deserves the same modeling discipline we apply to production systems.
The first day should feel like coffee-drinking onboarding. The newcomer starts the machine, authenticates, sees VS Code already installed, opens the feature the team is discussing, and spends the first real conversation with colleagues on the product. Not on certificates. Not on Git SSH errors. Not on SDK drift. Not on why the setup page says Node 20 but the build needs Node 22.
The company should be able to say, before the newcomer arrives:
given three fresh instances
when we run Welcome Setup-as-code
and we clone the project
and we build it
and we switch between the prepared instances
then the workstation profile is ready to welcome a developergiven three fresh instances
when we run Welcome Setup-as-code
and we clone the project
and we build it
and we switch between the prepared instances
then the workstation profile is ready to welcome a developerThat sentence is not poetry. It is the beginning of a DSL.
An instance here is a ready, built instance of the project itself — cloned, restored, built, runnable. Having two or three prepared at once is what lets a developer hold a feature on one and the develop integration on another, and switch between them as the work — increasingly, as parallel AI coding agents — advances. Part V shows how those instances are produced, as worktrees on a prepared machine or as boxes from a shared registry.
Scope: Two Phases of Onboarding
Workstation onboarding has two phases, and this series is deliberately about the second one.
Phase 1 — Provisioning. The enterprise turns hardware into a managed corporate machine: Active Directory or Intune domain join, the corporate image, disk encryption, the security baseline, MDM policy — and, decisively, the rights the developer will have. On macOS this is Jamf; the shape is the same. Phase 1 is owned by IT. This series assumes it, and does not try to cover it.
Phase 2 — Developer bootstrap. Once the developer is authenticated on that provisioned machine, something has to converge it from "a corporate laptop" to "a machine that builds our product": toolchain, SDKs, editor, developer certificates, clone, restore, build. That is the machine part, and it is this series' target. The welcome runner is a Phase-2 tool.
The seam between them matters. Phase 1 decides the privilege envelope — whether the developer is a standard user, when they can elevate, what a package manager is even allowed to do. Phase 2 must work inside that envelope rather than wish it away, which is why privilege is named as a boundary in Part III.
The seam can also be a collaboration rather than a wall. A mature organization embeds Phase 2 into Phase 1: IT asks each team to own and publish its setup module, then integrates and tests those modules into the corporate image itself. This is inner-source onboarding. The mechanism is shared scopes — IT owns a base scope and the integration, each team owns a named slice of the contract — and pub/sub: a team publishes a versioned, tested module, and IT subscribes, integrates, and re-gates it. That re-gating is the Part VI candidate lane applied to organizational contributions; IT proves a published module on fresh instances before it ships, exactly as it would prove a version transition.
It only works because a contribution arrives as a tested welcome.yaml with welcome doctor evidence against a shared surface. Without that, embedding team scripts into the corporate image is just the old fragile thing at larger scale. With it, the seam closes the loop: when Phase 2 is folded into the image, the first day reaches its strongest form — the setup already ran during imaging, so the newcomer logs into a machine that is already proven. The same module a team publishes here is the same artifact it could publish as a box to the registry in Part V.
OS-Agnostic by Design
This series is extracted from a real system — the "machine part" of developer onboarding I shipped inside a large CAC40 industrial group. That system was deliberately Windows-specific; Part IV tells its story and the lesson stepping back from it taught me.
The lesson is one sentence: onboarding is a language-agnostic contract with platform-specific adapters, not a Windows script, a Bash script, or a C# program. A welcome.yaml file describes expected tools, versions, repositories, probes, variables, runner matrices, and evidence; platform adapters translate that same intent into winget, apt, apk, brew, certificate-store changes, and editor configuration. Confuse the contract with one of its implementations and the bet collapses the day a developer arrives on another platform. Part IV makes the mechanism concrete.
Test your onboarding.
Why Companies Need This
Every company that hires developers already has an onboarding system. The only choice is whether it is explicit.
In a large CAC40 group, a developer blocked by an out-of-date or misconfigured workstation is not a small inconvenience. It is paid engineering capacity sitting idle. The salary is still running. The project plan is still moving. The meeting calendar is still full. The support queue gets another ticket. A senior engineer often gets interrupted to explain the workaround. The cost is not only the blocked newcomer; it is the chain of people pulled into a preventable setup failure.
Large companies can sometimes absorb that waste because they have buffers: platform teams, IT support, spare machines, internal documentation, and enough people that one blocked engineer does not stop the whole delivery flow. But that does not make the waste acceptable. It just makes it easier to hide.
For a small company, a TPE, or a PME, the same failure is harsher. There is no deep bench. The one backend developer who cannot build may be the person needed for the client demo. The founder may become first-line support. A half-day lost to certificates, SDK drift, Docker, or Git access can be a visible delivery risk. Small organizations do not have the financial or operational slack to treat workstation readiness as folklore.
That is why this should become a commodity. A company should not need heroic internal knowledge to answer "can a developer build this project on day one?" It should have a repeatable setup contract, a runner, fresh-instance evidence, and a doctor command. The valuable engineering work is the product, not rediscovering how to install the product's toolchain.
If the system is implicit, onboarding lives in old wiki pages, Slack memories, tribal knowledge, half-working scripts, and the one laptop image that nobody dares update. Every newcomer pays the same tax. Every update can break the first day. Every "it works on my machine" becomes an onboarding risk.
If the system is explicit, onboarding becomes testable:
| Company need | Setup-as-code answer |
|---|---|
| Paid engineering time is not wasted on setup drift | Fresh-instance evidence catches workstation failures before people are blocked |
| Large-company support load is reduced | Diagnostics classify failures before they hit the newcomer or senior engineers |
| Small-company delivery risk is reduced | A repeatable setup contract replaces fragile memory and founder-led rescue |
| Newcomer can build on day one | Fresh-instance CI proves clone, restore, build, test, and run |
| Tool updates do not surprise the team | Isolated lanes and matrices test version changes before adoption |
| Physical limits stay visible | Wi-Fi, badge, room, identity, and Git access become named boundaries |
| Support load is reduced | Failures produce diagnostics before the newcomer sees them |
| Knowledge does not drift | Tool versions, probes, and setup phases have one source of truth |
This is the same principle as Don't Put the Burden on Developers: if a failure can be checked by a system, the burden belongs on the system. A new developer should not be the test runner for your workstation policy.
DDD, Not Scripts First
The first move is not "choose Bash or PowerShell." The first move is Domain-Driven Design.
Name the domain. Name the bounded contexts. Name the events. Name the invariants. Name what "ready" means in company language.
Once the language stabilizes, the implementation follows:
PhysicalAccess: room, desk, badge, power, Wi-Fi.IdentityAccess: account, MFA, SSH key, certificate, Git permission.WorkstationBootstrap: OS profile, package manager, IDE, shell, corporate root certificates.ToolchainBaseline: pinned versions of Git, Docker, Podman, SDKs, CLIs, extensions.ProjectReadiness: clone, restore, build, test, run, debug, open the first feature.UpdateLane: controlled transitions such as3.3.2.2 -> 3.3.4.3.SetupContract: the structured source of truth that runners compile into actions.PlatformAdapter: the provider that maps contract intent towinget,apt,apk,brew, or corporate APIs.
This is where the DSL starts to appear. A team first says "during setup" in conversation. Then it writes "during setup" in a runbook. Then it uses the phrase in CI job names. Then it becomes a typed phase in a setup model. That is how operational knowledge turns into a language.
Setup-as-code Needs SOLID, DRY, and Modularity
A setup script that grows for two years without design becomes the new wiki: central, fragile, and feared.
Welcome Setup-as-code has to be engineered like production code:
| Principle | Setup-as-code consequence |
|---|---|
| Single Responsibility | Each installer, probe, and readiness check owns one concern |
| Open/Closed | Add a new tool or runner without rewriting the whole bootstrap |
| Liskov Substitution | VM, container, LXC, and real workstation runners honor shared contracts where they can |
| Interface Segregation | A Wi-Fi probe does not pretend to expose Docker capabilities |
| Dependency Inversion | Setup logic depends on ports: package manager, Git server, identity provider, filesystem |
| DRY | One source of truth for versions, probes, update transitions, and project readiness |
| Modular | Setup composes from versioned modules that publish how-to, sourced as a directory or a git ref (#b: branch, #cr: commit, #t: tag) |
SOLID and DRY keep one contract clean; modularity keeps it from having to be one contract at all. A module is a publishable unit of how-to — how to install a tool, probe a boundary, or stand up a cluster — that a team owns and a profile imports by reference. Each module publishes configuration facets, and a factory turns a chosen facet into a configured component the runner uses. That is Open/Closed lifted from classes to modules: extend the system by publishing a module, not by editing the monolith. Part VII makes the references, facets, and factory concrete.
That source of truth should be structured. The setup contract plays the same role Terraform configuration plays for infrastructure: a declarative file states desired reality, and providers/adapters make it real on a specific platform.
That connects this series to the broader site theme: operational knowledge is not special. It can be modeled, typed, generated, checked, and composed. The Ops DSL Ecosystem says this for operations at large. This series applies the same pressure to the first developer experience.
Part I: The Company Need
Every newcomer does setup. Every company needs repeatable onboarding. The first day is a company capability, not a personal scavenger hunt.
Part II: DDD for Onboarding
Developer setup has bounded contexts, aggregates, events, invariants, and a ubiquitous language. Model those before choosing the shell.
Part III: Physical Boundaries
Room, badge, Wi-Fi, identity, Git access, and workstation are not "edge cases." They are physical and institutional boundaries that setup-as-code must name.
Part IV: Installation as CI
Setup becomes an idempotent, testable, evidence-producing pipeline: verify, install, probe, build, run, report.
Part V: CI the Installation — Packer and Vagrant
How the CI actually runs, and where fresh instances come from. Packer builds a proven Windows machine and publishes boxes to a registry; developers stand up project instances as worktrees or boxes and switch between them. DevOps that terraforms a workstation, not a cloud.
Part VI: Updates and Version Transitions
Pinning is the baseline. Update discipline means testing isolated transitions and required version matrices before they reach a newcomer.
Part VII: The Onboarding DSL
Once a team can name during setup, during first clone, during update, and ready to build, those phrases can become a language-agnostic welcome.yaml contract.
Related Posts
- Domain-Driven Design - ubiquitous language, bounded contexts, invariants.
- Don't Put the Burden on Developers - move repeatable failure checks from humans to systems.
- Ops DSL Ecosystem - operational knowledge as typed, compiler-enforced language.
- Micro-DSLs and a Shared Kernel - decomposing a domain into bounded micro-DSLs.
- GitLab.Ci.Yaml - typed CI pipelines and version-aware infrastructure.
- Typed Docker - tool version drift, typed command surfaces, and testable infrastructure wrappers.