Git's Object Model & Workflows
Git is a content-addressable filesystem. Every piece of data is stored as an object identified by its SHA-1 hash. Understanding the four object types and how they compose is the key to mastering Git.
Stores file contents. No filename, no metadata -- just raw bytes identified by SHA-1. Two files with identical content share one blob.
A directory listing. Maps filenames to blob hashes (and sub-trees). Represents a snapshot of the project's directory structure.
Points to a tree (project snapshot), parent commit(s), author, committer, and message. Forms the nodes in Git's DAG.
An annotated pointer to a commit (or any object). Includes tagger identity, date, and a message. Lightweight tags are just refs.
The DAG, Refs & Index
Commits form a Directed Acyclic Graph (DAG) -- each commit points to its parent(s), never creating cycles. Branches and tags are simply refs: mutable pointers (files in .git/refs/) to commit hashes. HEAD is a symbolic ref pointing to the current branch.
The index (staging area) sits between your working directory and the repository. git add writes blobs and updates the index; git commit converts the index into a tree and creates a commit object.
Merge vs Rebase
| Aspect | Merge | Rebase |
|---|---|---|
| History | Preserves full branch topology (merge commit) | Produces a linear history |
| Conflict resolution | Once, in the merge commit | Per-commit during replay |
| Shared branches | Safe to use anytime | Never rebase published/shared commits |
| Use case | Feature integration, preserving context | Cleaning up local work before merging |
git bisect performs a binary search through commit history to find the exact commit that introduced a bug. You mark a known-good and known-bad commit, and Git checks out the midpoint for you to test. This reduces an N-commit search to O(log N) steps. It can be fully automated with git bisect run <test-script>.
CI: Fast Feedback on Every Commit
Continuous Integration is the practice of merging developer work into a shared mainline frequently -- ideally multiple times per day. Each integration triggers an automated pipeline that builds, tests, and reports results within minutes.
The Fast Feedback Loop
The core principle: developers should know within 10 minutes if their change broke something. This requires fast builds, parallelized test suites, and caching of dependencies and build artifacts. If CI is slow, developers batch changes and integration becomes painful.
Static Analysis (SAST)
Static Application Security Testing analyzes source code without executing it. Tools like SonarQube, Semgrep, and CodeQL scan for security vulnerabilities (SQL injection, XSS, hardcoded secrets), code smells, and style violations. SAST runs early in the pipeline because it needs no running environment.
Dependency Scanning (SCA)
Software Composition Analysis examines your dependency tree (package-lock.json, requirements.txt, go.sum) against known vulnerability databases (CVE/NVD). Tools like Dependabot, Snyk, and Trivy alert on vulnerable transitive dependencies and can auto-generate upgrade PRs.
CD: From Build to Production
Continuous Delivery ensures that code is always in a deployable state and that releasing to production is a one-click decision. Continuous Deployment goes further: every change that passes the pipeline is automatically deployed with no human gate.
| Aspect | Continuous Delivery | Continuous Deployment |
|---|---|---|
| Production release | Manual approval / button click | Fully automated |
| Risk tolerance | Lower -- human reviews each release | Higher -- relies on automated quality gates |
| Feedback speed | Fast (hours) | Fastest (minutes) |
| Prerequisite | Solid CI + staging environment | Solid CI + canary/rollback automation |
Pipeline Stages
Artifact Management
Build artifacts (Docker images, JARs, binaries) are stored in a versioned registry (Docker Hub, ECR, Artifactory, Nexus). Each artifact is immutable and tagged with the commit SHA or semantic version. The same artifact that passes staging is the one deployed to production -- never rebuild for a different environment.
Environment Promotion
Code progresses through environments: dev → staging → production. Configuration differs per environment (via env vars or config files), but the artifact stays the same. This guarantees that what you tested is what you deploy. Promotion can be automated (continuous deployment) or gated by manual approval.
Rolling, Blue-Green, Canary & More
How you roll out changes to production determines your blast radius when things go wrong. Each strategy trades off between speed, safety, and infrastructure cost.
Replace instances one-by-one (or batch-by-batch). Zero downtime, but old and new versions coexist briefly. Default in Kubernetes Deployments.
Run two identical environments. Deploy to the idle one ("green"), test it, then switch the load balancer. Instant rollback by switching back. Requires 2x infrastructure.
Route a small percentage of traffic (1-5%) to the new version. Monitor error rates and latency. Gradually increase traffic if metrics look good. Rollback is trivial.
Deploy code to all users but toggle features on/off at runtime. Decouples deployment from release. Enables A/B testing and gradual rollouts without redeploying.
Database Migrations
Schema changes are the hardest part of zero-downtime deployments. The key principle: make migrations backward-compatible. Use the expand-contract pattern:
IaC: Terraform, CloudFormation & GitOps
Infrastructure as Code (IaC) means defining servers, networks, and services in version-controlled configuration files instead of clicking through consoles. This makes infrastructure reproducible, reviewable, and auditable.
Declarative, provider-agnostic. Uses HCL. Maintains a state file that maps config to real resources. Plan before apply. Multi-cloud support.
AWS-native IaC. JSON/YAML templates. Manages stacks with automatic rollback on failure. Deep AWS integration but single-cloud only.
Define infrastructure in real programming languages (TypeScript, Python, Go). Full IDE support, loops, conditionals. Uses a state backend like Terraform.
Git as the single source of truth. ArgoCD/Flux watch a repo and reconcile cluster state to match. Pull-based model. Audit trail is the commit log.
Terraform State
Terraform's state file (terraform.tfstate) is a JSON mapping between your HCL resources and real-world infrastructure IDs. It enables terraform plan to diff desired vs. actual state. State must be stored remotely (S3, GCS, Terraform Cloud) with locking (DynamoDB) to prevent concurrent modifications.
# Terraform remote state backend
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
GitOps: ArgoCD & Flux
GitOps applies the Git workflow to infrastructure. The desired state of your Kubernetes cluster is declared in a Git repository. A controller (ArgoCD or Flux) continuously watches the repo and reconciles the cluster to match. Drift detection is automatic -- if someone manually changes the cluster, the controller reverts it.
Immutable Infra & Ansible
Two contrasting philosophies for managing servers: treat them as cattle (immutable, replaceable) or pets (mutable, carefully maintained). Modern practice strongly favors the cattle approach.
| Aspect | Mutable (Pets) | Immutable (Cattle) |
|---|---|---|
| Updates | SSH in, run commands, patch in place | Build new image, replace old instances |
| Drift | High risk -- servers diverge over time | None -- every instance is from the same image |
| Rollback | Difficult (undo scripts, hope for the best) | Trivial (deploy previous image) |
| Debugging | Check each server individually | All instances identical, reproduce locally |
| Tools | Ansible, Chef, Puppet | Packer + Terraform, Docker, AMIs |
Immutable Infrastructure
With immutable infrastructure, you never modify a running server. Instead, you bake a new machine image (AMI, Docker image) with every change, test it, then replace running instances. This eliminates configuration drift and makes rollback as simple as pointing to the previous image version.
Configuration Management with Ansible
Ansible is an agentless configuration management tool that uses SSH to push configuration to servers. It uses YAML playbooks to declare desired state. While it's traditionally a "mutable infrastructure" tool, it's still widely used for provisioning base images and for environments where full immutability isn't practical.
# Ansible playbook example
- hosts: webservers
become: yes
tasks:
- name: Install nginx
apt:
name: nginx
state: present
- name: Copy config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
Test Yourself
terraform plan to diff desired state against actual state and determine what changes to make.