Start with the Language
The mistake is to begin with Bash, PowerShell, Ansible, Chocolatey, Winget, Docker, or Terraform.
Those are implementation choices. The first question is: what is the domain?
In Domain-Driven Design, the model starts with a ubiquitous language. The team names the things that matter until developers, managers, platform engineers, and support staff can talk without translating every sentence.
Developer onboarding has that same need. A company needs shared words for:
- "the workstation is ready"
- "Git access has been verified"
- "the toolchain is pinned"
- "the first clone succeeded"
- "the project builds"
- "the setup is replayable"
- "the update from
3.3.2.2to3.3.4.3is safe"
Without names, every setup failure becomes an anecdote. With names, failures become domain events, states, and invariants.
Bounded Contexts
Onboarding is not one context. Treating it as one big script is the operational version of a god object.
The domain cuts naturally:
| Bounded context | Owns | Does not own |
|---|---|---|
PhysicalAccess |
room, desk, badge, power, Wi-Fi reachability | Git permissions |
IdentityAccess |
account, MFA, SSH key, certificates, groups | SDK versions |
WorkstationBootstrap |
OS profile, package manager, shell, editor | product build logic |
ToolchainBaseline |
pinned tools, version matrix, install policy | human access approval |
ProjectReadiness |
clone, restore, build, test, run, debug, local environment / cluster provisioning | building access cards |
UpdateLane |
candidate versions, transition probes, rollback decision | first-day social onboarding |
Each context has its own model. A Wi-Fi probe is not a Docker probe. A Git SSH key is not a package manager version. A project build is not an identity enrollment.
The contexts integrate through explicit contracts:
PhysicalAccess emits WifiVerified.
IdentityAccess emits GitAccessVerified.
ToolchainBaseline emits ToolchainPinned.
ProjectReadiness consumes those events and emits ProjectBuilt.PhysicalAccess emits WifiVerified.
IdentityAccess emits GitAccessVerified.
ToolchainBaseline emits ToolchainPinned.
ProjectReadiness consumes those events and emits ProjectBuilt.The same publish/consume pattern lifts one level. Where contexts integrate inside a machine by emitting and consuming events, teams integrate across the organization by publishing setup modules that IT subscribes to and integrates — the collaborative seam from the series introduction. It is the same contract discipline, with a larger blast radius.
This is where setup-as-code becomes much easier to reason about. The implementation can be iterative because each context can improve without rewriting the whole pipeline.
Events and Invariants
A setup domain should not rely on vague status text.
It should speak in events:
| Event | Meaning |
|---|---|
DesktopPrepared |
the target workstation profile exists |
WifiVerified |
network path required for setup is reachable |
GitAccessVerified |
the configured identity can clone the required repository |
ToolchainPinned |
required tools match the baseline |
ProjectBuilt |
the real project builds on the prepared instance |
InstanceSwitchedSuccessfully |
the team can use another fresh prepared instance without manual repair |
Then it should define invariants:
- A workstation is not ready until the project has built from a fresh clone.
- A toolchain is not approved until all required versions are pinned or explicitly floated.
- An update is not adopted until it passes the same probes as the current baseline.
- A physical boundary is not hidden inside a script; it is represented as a named prerequisite or probe.
- A failure without diagnostic evidence is itself a failure of the setup system.
That last point matters. The setup runner is not only responsible for doing work. It is responsible for explaining what failed.
Why This Becomes a DSL
The language starts in conversation:
"During setup, install VS Code before opening the project."
"During first clone, verify SSH and the corporate certificate chain."
"During update from 3.3.2.2 to 3.3.4.3, test Docker separately."
"Given three fresh instances, prove we can build and switch.""During setup, install VS Code before opening the project."
"During first clone, verify SSH and the corporate certificate chain."
"During update from 3.3.2.2 to 3.3.4.3, test Docker separately."
"Given three fresh instances, prove we can build and switch."Then the same language appears in CI job names. Then in a YAML file. Then in a typed model. Then in analyzers. That is the familiar pattern from the site's DSL articles: stable operational vocabulary becomes a language.
The Ops DSL Ecosystem argues that operational knowledge deserves typed surfaces. Micro-DSLs and a Shared Kernel shows how a domain decomposes into bounded micro-DSLs instead of one monolith.
Welcome Setup-as-code follows the same path. It is not "just automation." It is the beginning of an onboarding DSL.
A setup domain you can name is a setup domain you can test.
Test your onboarding.