At work, one of the things that I have always been a huge advocate for is allowing the developer to dictate their own setup. This is because, for a skilled developer, it might be something that they might be more comfortable with debugging and working around their own setup, rather than a generic setup that might be more difficult to work with.
Should we provide a default setup?
Absolutely! One of the things that is the quickest for onboarding new developers, is being able to provide them a setup that just works out of the box. This helps get new developers up to speed and get something working.
The issues with default setups
Complex setups come with a big bit of invisible technical debt: debugging.
Let’s have a look at the example where we have a default docker-driven setup that is provided, but it is designed to work with many legacy systems, and is highly customisable. A common approach in a team-led setup is writing scripts that read a configuration file and only load what’s configured.
The problem comes when another developer makes a change - a core build tooling upgrade like Node, or even a PHP bump - and nobody else is aware of it. Unless you’re constantly pulling and reading what changed, you find out when something in your environment stops matching.
Do we want to push out a dependency upgrade, and a week later have one developer pulling in another to debug an issue that was already solved? That costs time and resources for not just the developer who hit the issue, but the second one too, especially with no context on it.
Would communication solve this?
To an extent, yes. But communication runs into the question of what to actually tell the wider team. There’s a lot that could be communicated, and if you communicate everything, people tune it all out, and we fall back into the problem above: hoping whoever hits the issue finds the message.
If we go to trying to communicate any critical changes, the question then becomes, what is a critical change? Is it a core dependency upgrade? Is it a change to how we accept variables? Realistically what is important to one developer might not be important to the next, and it becomes a problem where, realistically, it comes down to each developer deciding what matters to them.
Security is something we should always strive to be aware of. When you focus on it, dependency upgrades happen a lot, and most of the time they’re completely benign. But you might have a setup built on an out-of-date system or build process that blocks you from updating, and unblocking that should never depend on making everyone aware they need to update.
Recommended, not required
To me, having the ability to define my own setup in a corporate and enterprise environment gives me the benefit of promoting a culture of debugging and developing my own setup. If I was to rely on a team setup, the ability to modify it, or propose modifications would really be a scary problem, and even more so for new people to the business & team.
If we define a setup that is led by the team as a whole as the default “recommended” setup, but allow others to define their own, we have the best of both worlds. We don’t restrict people who like to work in their own way, but we also provide a quick path to production for new developers.
Obviously, this would come with some trade-offs, if a developer decides to make their setup their own way, they inherit the burden of maintaining it, keeping track of what changes, and being able to debug their environment.
The honest trade-offs
I don’t think any of this is free, and it would be dishonest to pitch it that way. If you own your setup, you take on a few things that a team setup handles for you:
- Shared debugging knowledge goes away. If my environment is mine, nobody else can reproduce it, and “it works on the team setup” stops being useful information for either of us.
- Support becomes my problem. When something breaks, I can’t reasonably expect whoever maintains the default setup to drop what they’re doing and fix mine.
- Security updates become my responsibility. A team setup can push a patch out to everybody at once; my own setup only gets patched because I decided to patch it.
- Drift from production is now per-person. Instead of one known gap between the default setup and production, there are as many gaps as there are developers.
The last one is the one I take most seriously, and it’s why the pipeline - not anyone’s machine - has to be the thing that matches production.
An example of my own setup
At work, I primarily work with a team that has their own Makefile, bash and docker compose based setup, but locally? I personally use Laravel Herd, Mise and Docker as my own setup.
Docker is used as a base for database and some core services that I don’t want to have to maintain, or I want to be able to rebuild from scratch quickly. But I run most of my development setup via Herd with Mise as the alternative to nvm or asdf for managing my Node and NPM versions.
I did this because I like to be able to quickly spin up a new project as a proof of concept for something at work, but also be able to dig into things, without having to remember which container I need to exec a command in, to be perfectly sure, and then also the constant problem that comes up with docker exec where the command might not even be installed.
Does this match production? Not really, but the main things that need to match production are things I keep myself aware of at a core level, and I make sure our CI/CD pipeline is as close to production as possible, catching the last differences in the build and test steps.
Conclusion
My opinion is that, while providing a default recommended setup is always a great idea, allowing a developer to define their own tooling, and also their own development environment is the best thing. This should always be balanced with security obligations, and if allowed, they should also be happy to communicate and update their own setup with security updates constantly.
If you’re the one making this call for a team: provide a default that works from a cold clone, document the handful of things that genuinely have to match production, require security updates regardless of what someone runs locally, and let people opt out on the condition that they own what they opt into.
One thing I didn’t touch on but also still believe in, is that allowing developers to explore their own setups can also promote trying out and using new tools, possibly finding better ways of working, I would have never ended up trying Mise without that freedom, and it has since become something I use all the time.