Introduction to CI/CD
- CI catches integration bugs early — on every push, not at release time.
- Continuous Delivery: every successful build is deployable. Continuous Deployment: it deploys automatically.
- Pipeline stages: lint → test → build → deploy. Each stage gates the next.
CI (Continuous Integration) automatically builds and tests code on every push. CD (Continuous Delivery) automatically produces a deployable artifact after every successful CI run. Continuous Deployment goes one step further — automatically deploying to production without human approval. A CI/CD pipeline catches bugs early, reduces deployment risk, and enables fast iteration.
A GitHub Actions CI Pipeline
# .github/workflows/ci.yml name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Cache pip packages uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - name: Install dependencies run: pip install -r requirements.txt - name: Run linter run: ruff check . - name: Run tests with coverage run: pytest --cov=. --cov-report=xml - name: Upload coverage uses: codecov/codecov-action@v4
Adding CD — Automatic Deployment
# Add to the same file — deploy job runs after test passes deploy: needs: test # only run if test job passes runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' # only deploy from main steps: - uses: actions/checkout@v4 - name: Build Docker image run: | docker build -t myapp:${{ github.sha }} . docker tag myapp:${{ github.sha }} myapp:latest - name: Push to registry run: | echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin docker push myapp:${{ github.sha }} docker push myapp:latest - name: Deploy to staging run: | # SSH to server and pull new image ssh -o StrictHostKeyChecking=no deploy@${{ secrets.STAGING_HOST }} \ "docker pull myapp:latest && docker compose up -d"
🎯 Key Takeaways
- CI catches integration bugs early — on every push, not at release time.
- Continuous Delivery: every successful build is deployable. Continuous Deployment: it deploys automatically.
- Pipeline stages: lint → test → build → deploy. Each stage gates the next.
- Secrets in CI must be stored as encrypted environment variables — never hardcode credentials.
- Fast feedback loop is the goal — a CI pipeline longer than 10 minutes loses its value.
Interview Questions on This Topic
- QWhat is the difference between CI and CD?
- QWhat stages should a good CI/CD pipeline have?
- QWhat is the difference between Continuous Delivery and Continuous Deployment?
Frequently Asked Questions
What is the difference between Continuous Delivery and Continuous Deployment?
Continuous Delivery means every successful build produces an artifact that could be deployed — but a human decides when. Continuous Deployment goes all the way: every successful build is automatically deployed to production with no human approval step. Most teams practice Continuous Delivery for production (human approval gate) but Continuous Deployment to staging.
What should a good CI pipeline include?
Minimum: linting, unit tests, integration tests. Better: security scanning (SAST), dependency vulnerability check, test coverage threshold enforcement, and Docker image build verification. For production services: end-to-end tests in a staging environment before deploying to production.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.