Development
Key points

Key points

  1. 1Continuous integration means merging small changes into one shared branch often, with an automated build and test run that tells you within minutes if something broke.
  2. 2A small team needs only a short pipeline at first: install, lint, test, build once, deploy to staging, then promote the same artifact to production.
  3. 3Fast recovery matters more than perfect prevention, so plan rollbacks before you need them and keep database changes compatible with the previous release.
  4. 4Secrets, permissions and pipeline speed need deliberate care, because a slow or leaky pipeline gets bypassed.

What CI and CD actually mean

Continuous integration is a team habit before it is a tool. GitHub's documentation defines it as a software practice that requires frequently committing code to a shared repository, where committing more often detects errors sooner and reduces the amount of code to debug when something fails.

The site trunkbaseddevelopment.com traces the idea back to Kent Beck's Extreme Programming and makes a point many teams miss: the original intent was a single integration point for the developer team, integrated daily or more often. A CI server that runs tests on branches that live for weeks is automation without integration.

Continuous delivery and continuous deployment extend the idea to releases.

TermWhat it meansWho presses deploy
Continuous integrationEvery change merges into the shared branch often and is built and tested automaticallyNobody, it is about merging
Continuous deliveryThe shared branch is always in a releasable state and a release is one action awayA person, when they choose
Continuous deploymentEvery change that passes the pipeline goes to production automaticallyThe pipeline

Most small teams do well with continuous delivery to production and continuous deployment to staging. Every merge lands on staging automatically, and production releases happen by an explicit action several times a week or several times a day.

Choose a branching model that keeps main releasable

JetBrains describes trunk-based development as committing changes directly to the main branch, the trunk, instead of developing features or bug fixes in separate branches and merging them at some later stage. The goal is to keep the main branch healthy and releasable at all times.

For a small team the practical version is this.

  1. Branch per change, not per feature. Open a branch, make one focused change, open a pull request the same day or the next.
  2. Merge within a day or two. Travis CI's explainer notes that the longer developers take, the harder the merge, and that developers would have forgotten what they had done by the time they resolve conflicts.
  3. Hide unfinished work behind a flag. If a feature takes two weeks, merge it in small pieces that are switched off in production.
  4. Fix a broken main before anything else. A red main blocks everyone, so the person who broke it fixes or reverts it immediately.

Avoid long-lived develop or release branches unless you ship software that customers install and you must patch old versions. For a web application, one main branch plus short-lived branches is enough.

The first pipeline

Start with a pipeline that runs on every pull request and every push to main. It should answer one question quickly: is this change safe to merge?

GitHub Actions is the natural starting point for teams that already host code on GitHub. The documentation explains that workflows can run on GitHub-hosted machines or your own, and that GitHub runs your CI tests and provides the results of each test in the pull request. Workflows live as YAML files in .github/workflows/, and the workflow syntax reference documents every key.

A first workflow for a Node.js project looks like this.

name: ci
on:
  pull_request:
  push:
    branches: [main]

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<pinned-sha>
      - uses: actions/setup-node@<pinned-sha>
        with:
          node-version-file: .nvmrc
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

The concurrency block cancels an older run on the same branch when a newer commit arrives, which saves minutes and avoids confusing results. Pin each action to a specific release or commit SHA, shown here as a placeholder, so an upstream change cannot alter your pipeline without a commit in your repository.

Then add stages in this order as the project grows.

StageRuns onPurpose
Lint and type checkEvery pull requestCatch mistakes in seconds
Unit testsEvery pull requestVerify logic without external services
BuildEvery pull requestProve the artifact can be produced
Integration testsEvery pull request or merge to mainCheck the code against a real database or service
Deploy to stagingMerge to mainMake every change visible in a shared environment
Smoke tests on stagingAfter staging deployConfirm the deployed app starts and key pages respond
Deploy to productionManual approval or tagRelease the tested artifact

Keep one build script for laptops and CI. As trunkbaseddevelopment.com puts it, the build script which developers run prior to checking in, is the same one which is followed by the CI service. Put the commands in package.json, a Makefile or a task runner, and have the workflow call them. CI then verifies the same thing you checked on your laptop.

Build once and promote the same artifact

A common mistake is building the application separately for staging and for production. Each build can pull slightly different dependencies or pick up different environment settings, so the thing you tested is not the thing you shipped.

Harness describes the alternative as building once and promoting the same artifact through every stage of testing and deployment, a practice it calls artifact immutability. In practice:

  • Build a container image, a zip or a static bundle once, tagged with the commit SHA.
  • Store it in a registry or artifact store.
  • Deploy that exact tag to staging, test it, then deploy the same tag to production.
  • Inject environment differences such as database URLs and API keys at runtime, never at build time.

Tagging by commit SHA also gives you a clean audit trail. Anyone can answer "what is running in production" by reading one tag and finding the commit.

Plan rollbacks before you need them

Every team eventually ships a bad release. What matters is how long it takes to recover. Octopus Deploy's guide to rollback strategies starts with an uncomfortable scenario: the rollback scripts were never tested, and the last rollback happened years ago. Its advice is to measure strategies by recovery time, from problem found to service restored.

Three principles from that guide fit small teams well.

  1. Roll back code by redeploying the previous artifact. If you build once and tag by commit, rolling back is deploying the previous tag. Octopus describes a simple strategy that redeploys the previous version and skips database steps, and notes that a breaking schema change prevents rollbacks, leaving only a roll forward.
  2. Do not roll back the database. The guide warns that both rollback scripts and backup restores will likely lose data written since the release.
  3. Make database changes compatible with the previous release. Deploy schema changes before the code that needs them, using the expand and contract pattern: add the new column or table first, ship code that uses it, and remove the old structure only in a later release. Then the previous code version still works if you need to redeploy it.

Write the rollback steps down and run them once on staging. A rollback you have never practiced is a guess.

Secrets and pipeline security

A CI system can read your source code and deploy to production, which makes it one of the most valuable targets in your stack. Harness recommends starting with the basics: keeping secrets out of source control, limiting who can change pipelines and environments, and requiring SSO and multi-factor authentication.

For a small team that translates into a short list.

  • Store secrets in the CI provider's secret store, never in the repository, and never print them in logs.
  • Give each environment its own credentials. A staging key should not work against production.
  • Grant the pipeline the minimum permissions it needs. Test jobs need read access to the repository, and only the deploy job needs credentials that can change production.
  • Require approval for production deploys using protected environments or a manual gate.
  • Scan dependencies on every pull request so a known vulnerable package is flagged before merge.
  • Do not run untrusted pull request code with access to secrets, which matters most for public repositories.

Keep the pipeline fast and trusted

A pipeline that takes forty minutes or fails at random gets bypassed. GitLab's guide points to Martin Fowler's ten-minute build as a target most modern projects can reach, and notes that every minute saved is saved for every developer on every commit.

Ways to keep it fast:

  • Cache dependencies between runs.
  • Run independent jobs in parallel, such as lint, unit tests and type checks.
  • Split slow test suites across several runners. Harness suggests parallelizing so total test time depends on your infrastructure, not just the size of the codebase.
  • Push expensive end-to-end tests later in the pipeline, after the fast checks pass.

Ways to keep it trusted:

  • Treat flaky tests as bugs. Quarantine a flaky test immediately and fix it, because a team that learns to rerun failures stops reading them.
  • Measure the pipeline. Track how long runs take, where they fail and how often deploys need rollbacks, as Harness recommends, and fix the worst stage first.
  • Review failures without blame. GitLab advises looking for patterns in failed builds and making failures blameless, so people report problems rather than hide them.

Teams using AI coding agents benefit twice from a strong pipeline, because the agent gets the same fast, objective feedback a person does. Quanta Intelligence playbooks can encode the pipeline conventions above so agents write code that passes them on the first run.

FAQ

FAQ

Do we need CI/CD if there are only two of us?

Yes. Small teams have no one whose job is to catch forgotten steps, so automation does that job. A basic pipeline that lints, tests and builds every pull request takes an afternoon to set up and saves that time within weeks.

Should every merge to main deploy straight to production?

Not necessarily. A common setup deploys every merge to staging automatically and promotes the same build to production by an explicit action. Move to automatic production deploys once your tests, monitoring and rollback process give you confidence that a bad change will be caught and reversed quickly.

How long should a CI run take?

Aim for the fast checks on a pull request to finish within about ten minutes, the guideline Martin Fowler describes and GitLab cites. Longer runs push developers to batch changes or skip waiting, which undermines the point of CI. Cache dependencies, run jobs in parallel and move slow tests later in the pipeline.

How do we roll back a release that changed the database schema?

Redeploy the previous application version and leave the database alone, which only works if the schema change was backward compatible. Use the expand and contract pattern so the old code can still run against the new schema. If a change was not compatible, the fastest recovery is usually a roll forward with a fix.

Where should environment-specific settings live?

Outside the build artifact. Build one artifact, then inject database URLs, API keys and feature settings at deploy or run time from the CI secret store or the hosting platform. This keeps staging and production running identical code with different configuration.

Sources

Sources

  1. https://dev.to/_d7eb1c1703182e3ce1782/best-cicd-pipeline-for-small-teams-a-practical-2026-guide-2ad0
  2. https://docs.github.com/en/actions/get-started/continuous-integration
  3. https://trunkbaseddevelopment.com/continuous-integration/
  4. https://www.jetbrains.com/teamcity/ci-cd-guide/concepts/trunk-based-development/
  5. https://www.travis-ci.com/blog/explaining-trunk-based-development/
  6. https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions
  7. https://www.harness.io/blog/ci-cd-best-practices
  8. https://octopus.com/blog/modern-rollback-strategies
  9. https://about.gitlab.com/topics/ci-cd/continuous-integration-best-practices/
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles