I’ve been thinking a lot lately about how DevOps scales. DevOps, and related practices, get a lot of attention when it comes to scaling up at large organizations like Google and Netflix. But what about the other extreme of very small teams?
This is a list of DevOps practices I use on the tiny scale: Solo projects.
While most of these practices offer an immediate benefit, even for a single-person team, in most cases, the benefit grows as the team grows. That is to say, the ROI starts positive, and gets even better with larger scale.
Continuous Integration (CI) is the practices of merging code changes into mainline on a regular basis (at least daily). This is usually accompanied by running automated tests for every code change, prior to merging into the production branch. This implies that your code does have tests. If you don’t have tests yet, you can still set up CI (running an empty test suite), then begin adding tests from here on. This is also the perfect place to run your favorite linters.
This should usually be the first thing any team (or solo developer) sets up, whether on a new project, or when switching to DevOps practices.
Whether you’re using hosted version control, such as GitHub or GitLab, or hosting your own version control, setting up a continuous integration server is pretty straight forward. If you’re just getting started, I recommend GitLab with GitLab CI/CD, simply because the tools are pre-bundled, easy to configure, and either cheap or free, and with good documentation.
While this isn’t usually listed among DevOps practices, I believe it is the natural and best way to manage your development workflow. For large teams, it is often necessary to scale down GitFlow, or some other over-complex branching strategy. For a solo developer, this may mean scaling up. But I think it’s worth it!
In many solo projects, it’s common to simply commit to
master whenever a
change is ready. The change to Trunk-based development is a simple matter of
creating a branch before creating every change, then creating a pull request
and merging it, as if it were written by another person.
I do this for all of my personal projects, even this web site! The benefits may not be obvious at first, but let me outline several:
- It provides a clear start and stop of each piece of work.
- It keeps a clean git history, by keeping related commits together. This is especially useful when I may have a long-running feature that might otherwise be broken up with unrelated bug fixes or other minor changes.
- It makes it easy to review my own code. Yes, I review my own code. I don’t usually review my own code as rigorously as I do that of another developer, but I always do a quick check over my PR to make sure I haven’t accidentally committed debugging code, or unrelated changes, or that I’ve forgotten to include new files.
- Closely related to numbers 2 and 3 above, it makes it possible to rework pull requests before merging. I often squash, re-order, or even completely remove commits, prior to merging.
- And finally, if I ever accept pull requests from others (as happens occasionally on my open-source projects), I already have a clean work flow in place.
Any application that runs on a server should be configured for Continuous
Deployment. Apps to be installed (i.e. mobile apps, or
.deb packages) should
be continuously packaged (i.e. Continuous Delivery), rather than literally
For legacy projects with complex dependencies, this can be very difficult to put in place.
Fortunately, for most solo projects, this shouldn’t be an insurmountable
obstacle. And if you’re just starting out, it’s even easier. For all of my
new projects, I start with a dancing
skeleton. For this site, my
dancing skeleton was a simple, blank web page that just displayed my name. But
it was automatically deploying any time I merged to
master via GitLab!
Once I had my dancing skeleton in place, I then added a theme, and content.
This approach works nicely with Kubernetes and Helm, which happens to be my preference, but it can be just as well be used with Heroku, when deploying to a standard VM, or any other delivery mechanism.
Logging is another piece of the DevOps puzzle that is easily and helpfully employed by a solo developer. For one of my own apps, I use both loggly and Sentry’s free hosting plans. There are countless other options out there, too, and you can always host your own with open-source tools.
If you’re just starting a new project, this is the perfect time to add logging, because retrofitting logging into an existing app is always harder.
The key to logging, especially for the solo developer, is that it should help find bugs in production software quickly. This means that all unhandled error conditions should generate logs (good programming practice), and that each log should contain relevant information to pin-point the cause. This usually includes information like a stack trace, the name of the service that failed, and the context of the failure, such as the authenticated user, or the cron job that started the process.
This is not meant to be an exhaustive list of DevOps practices a solo developer can or should use. It’s only a starting point. One key tenant of DevOps is the concept of continuous improvement.
You should constantly be on the lookout for areas where you can improve your processes and habits.
Whenever you find yourself solving a problem the second or third time, consider if it can be automated or otherwise made easier or faster.
Is this the third time you’ve spent half an hour tracking down a bug? Consider whether better logging, or better tests could help speed up the process next time.
Have you struggled with out-of-memory errors before? Consider whether it’s time to add server monitoring and alerts.
There are countless “DevOps” practices in the wild, and no doubt many of them can be used by solo developers. What other practices do you employ in your solo projects? I’d love to read your comments.