CI/CD: Its Place in Public Health Software Teams
Table of Contents
Introduction #
A public health unit (PHU) builds a dashboard to track water test results. It works well - until someone updates the code and breaks a key feature. The fix takes hours, and no one’s quite sure what changed.
These kinds of setbacks are common. Public health teams often work under pressure, with limited time and technical support. But the tools we use to manage code - how we write it, review it, and deploy it - can make a big difference.
CI/CD (Continuous Integration and Continuous Deployment) and platforms like GitHub help teams avoid these pitfalls. They support collaboration, catch errors early, and make it easier to maintain reliable software over time.
What is CI/CD? #
Without automation, software workflows rely heavily on memory and manual checks. This can work, but it can be time-consuming and prone to human error, especially for complex projects. Many of these development tasks are routine and rule-based, making them well-suited to automation.
CI/CD is a set of practices that automates parts of the software development lifecycle. In plain terms:
- Continuous Integration (CI) means automatically checking code when it has changed - running tests to catch issues before they reach production and enforcing style or other rules.
- Continuous Deployment (CD) refers to automatically releasing code once it passes those checks, whether that’s publishing a dashboard or updating a model.
Though standard practice in software engineering for years, CI/CD is increasingly relevant to public health teams who write and share code. The reproducibility and quality control they offer can help our teams work consistently across contributors.
The Role of Platforms #
Platforms like GitHub host code and track changes while supporting collaboration through pull requests and reviews. They also integrate with CI/CD tools, making automation straightforward to set up and maintain.
Several of the benefits this can bring to PHUs, such as transparency and reproducibility, are elaborated on in Innovation in the Open.
Making it Practical: Pre-commit and GitHub Actions #
This section introduces two tools that make CI/CD practical for small teams: pre-commit and GitHub Actions. You don’t need a DevOps background to use them, just a shared codebase and a few minutes to set up.
Pre-commit checks code before it is committed. It takes care of local code hygiene and is easy to set up. You can use it to format code, catch syntax errors, or block commits that don’t meet project standards.
A few options for local setup:
- pre-commit - install via pip or conda
- prek - a compatible alternative; install globally with
uv tool install prek(docs)
GitHub Actions runs workflows in the cloud. It can lint code, run tests, compile projects, or send alerts - triggered by events like a push or pull request.
Together, these tools help teams enforce quality, supporting better habits and clearer code. They’re flexible enough for Python scripts, R projects, or even SQL workflows. And they integrate well with Git, which most teams already use.
Example Workflow #
Let’s say your team maintains a Python script within a GitHub repository that cleans and summarizes water test results. You want to make sure the code is clean and easy to review.
Here’s how a simple CI/CD setup might look:
Local Checks with Pre-commit #
To get started with local checks, you can install pre-commit and add a config file to your repo. For example, to set up a config file with linting for python and JSON:
# .pre-commit-config.yaml
repos:
- repo: https://GitHub.com/astral-sh/ruff-pre-commit
rev: v0.13.0
hooks:
- id: ruff-check
args: [--fix] # Lint and auto-fix
- id: ruff-format # Format code like black
- repo: https://GitHub.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: pretty-format-json
args: [--autofix]
files: \.json$
Then to set up these hooks in .git/hooks, run:
pre-commit install
Now, every time you try to commit code, pre-commit runs these tools: pretty-format-json (cleans up your JSON files) and Ruff (attempts automatic fixes for Python code errors and project-defined style issues).
If the code doesn’t pass, the commit is blocked until it’s fixed. For example, say your commit contains the following Python file:
import json,os
def load_data(path):
with open(path) as f:
data=json.load(f)
return data
def process(data):
result = [x for x in data if x['value']>10]
return result
And a JSON file:
{ "name":"Guelph", "value": 12, "status":"ok"}
Both files break the formatting rules defined in the pre-commit files. Once the pre-commit hooks are run, the commit will be blocked and the issues auto-fixed (if able). The resulting Python file will then be:
import json
import os
def load_data(path):
with open(path) as f:
data = json.load(f)
return data
def process(data):
result = [x for x in data if x["value"] > 10]
return result
With the new JSON file:
{
"name": "Guelph",
"value": 12,
"status": "ok"
}
These cleaned files can then be re-added and successfully committed. By blocking commits that violate the pre-commit rules, the codebase can be kept clean without needing manual review.
Though this scenario targets code linting, there are many additional hooks available - both from the pre-commit-hooks repo and other sources - that offer additional cleaning and error checking capabilities.
GitHub Actions for Cloud Automation #
GitHub Actions runs workflows in response to events like push/pull requests or scheduled jobs. It’s useful for enforcing standards and running tests on code before it reaches your remote repository.
Here’s a simple workflow that runs Ruff and pytest:
# .GitHub/workflows/lint-and-test.yml
name: Lint and Test
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install ruff pytest
- name: Run Ruff
run: ruff check . --fix
- name: Run Tests
run: pytest
This workflow ensures that every change is linted and tested before it’s merged. If something breaks, GitHub shows a clear error message.
Best Practices #
CI/CD works best when it’s part of a broader culture of care - care for code, for data, and for the people who use both. These practices help teams get the most out of automation:
Start Small: Begin with formatting and linting. Add tests and deployment steps gradually, as your team gets comfortable.
Keep Configs in Version Control: Store your
.pre-commit-config.yamland GitHub Actions workflows in the repo. This makes them visible, editable, and consistent across machines.Use Descriptive Failures: Make sure your tools give clear messages when something breaks. A cryptic error slows everyone down. A helpful one teaches.
Protect Main Branches: Use branch protection rules to require passing checks before merging. This prevents broken code from reaching production.
Avoid Over-Automation: Not everything needs a hook or a workflow. Automate what’s repetitive or error-prone. Leave room for judgment and context.
Review Periodically: As your team grows or your tools change, revisit your CI/CD setup. What helped last year might now be a bottleneck - or a blind spot.
Conclusion #
CI/CD and modern development platforms aren’t just for tech companies. They’re practical tools that help public health teams write better code and work together more effectively. The goal of CI/CD isn’t perfect pipelines, but dependable ones. For PHUs, where code often supports real-world decisions, this reliability matters.
Start with tools that have real impact for you and your team. Use pre-commit to catch and fix issues before code is committed and GitHub Actions to run checks and tests automatically when code changes.
Add a .pre-commit-config.yaml to your repo. Set up a basic workflow in .GitHub/workflows. Keep configs in version control, protect your main branch, and review your setup as your team grows.
These steps take minutes to set up and save hours of troubleshooting, helping your team keep code clean and catch issues early.
This kind of automation doesn’t replace testing or peer review. But it clears away the small stuff, and that’s time better spent responding to public needs and building tools that can be depended on.