Emmanuel Akinfulubi

The job all 3 written

Cloud Engineer, DevOps Engineer, SRE, Platform Engineer, Solutions Architect: what each title really means, and how to read an advert.

1 Five job titles, and what each one is really asked to do

Five job titles keep appearing on the same adverts, they overlap on paper, and companies use them differently from each other. The honest starting point is that no single person does all five jobs, and most adverts are written by someone who is not sure which one they are hiring for either.

The five roles, and what each one is really asked to do

Cloud Engineer

Builds and runs the infrastructure a company's software runs on.

  • Provisions compute, storage, networking: EC2, S3, VPCs, load balancers
  • Infrastructure as Code: Terraform, CloudFormation, Pulumi
  • Cost, capacity and picking the right managed service over the DIY one
  • Usually tied to one cloud vendor's specific services

The closest thing to "traditional sysadmin, but for the cloud." Deep on one vendor's console and CLI, less on the pipeline that deploys onto it.

DevOps Engineer

Builds the pipeline that gets a developer's code into production.

  • CI/CD: build, test, deploy, automatically, on every change
  • Containers and orchestration: Docker, Kubernetes
  • The cultural half of the job: fewer handoffs between "dev" and "ops"
  • Some cloud and IaC work, but the pipeline is the actual centre

Automation and process, more than infrastructure design. The title is also the most overloaded of the five: some companies mean "Cloud Engineer" when they say it, others mean it literally.

Site Reliability Engineer (SRE)

Applies software engineering to keep production up and fast.

  • SLOs, SLAs and error budgets: reliability as a number, not a feeling
  • On-call, incident response, blameless postmortems
  • Writes real code: tooling, automation, sometimes the product itself
  • Google-originated discipline with a specific, published methodology

The most rigorously defined title of the five, and also the most casually misused. An advert that says "SRE" but describes plain server admin with no mention of error budgets is not really hiring for the role.

Platform Engineer

Builds the internal product that other engineers use to ship.

  • Self-service infrastructure: a developer clicks a button, not files a ticket
  • Golden paths: a paved, supported way to deploy a new service
  • Treats other engineers as the customer of the platform team
  • Heavy Kubernetes, IaC and internal tooling

DevOps thinking, aimed inward, at a company's own engineers rather than at any one pipeline. The newest of the five titles, and the fastest growing.

Solutions Architect

Designs the system, often before a customer or team writes any of it.

  • Maps a business requirement onto specific cloud services
  • Diagrams, cost estimates, and trade-off decisions, in advance
  • Frequently customer-facing, at a cloud vendor or a consultancy
  • Broad knowledge of an entire cloud platform's service catalogue

Breadth over depth, and design over hands-on-keyboard. The role that talks to the business most directly of the five.

The one distinction that explains most of the confusion

Put the five on a line from "builds infrastructure" to "builds process" to "designs systems," and most of the ambiguity in job adverts is really just two roles' worth of responsibilities badly split across one title. A Cloud Engineer and a DevOps Engineer at a small company are frequently the same person with whichever title the company's pay bands happened to have. An SRE and a Platform Engineer at a large company can be different teams doing genuinely different work. A Solutions Architect is the one role here that is reliably distinct, because it is defined by who it talks to, not which tool it uses.

Where this course spends its time. The five roles share a floor: shell, Git, containers, cloud fundamentals, IAM, networking, IaC, CI/CD, Kubernetes, observability. That floor is most of this course, and it is genuinely shared -- a Platform Engineer and an SRE both need to read a Terraform plan and know why a security group with 0.0.0.0/0 on port 22 is a problem. The parts of each role that diverge (SRE's error-budget maths, Solutions Architect's cost-and-design work) are covered explicitly in their own lessons rather than assumed.

Reading an advert by its vocabulary, not its title

The fix is the same one the AI Engineer course uses: count the signals in the responsibilities, not the job title at the top.

Reading an advert by its vocabulary, not its title

ran at build time
# Which role does this advert actually describe? Count the signals, don't read the title.
declare -a SIGNALS_CLOUD=(vpc ec2 terraform iac "cost optimization" "well-architected" networking)
declare -a SIGNALS_DEVOPS=("ci/cd" jenkins "github actions" pipeline docker kubernetes automation)
declare -a SIGNALS_SRE=(slo sla "error budget" "on-call" incident postmortem toil)
declare -a SIGNALS_PLATFORM=("self-service" "golden path" "internal platform" "developer experience" backstage)
declare -a SIGNALS_ARCHITECT=(diagram "well-architected framework" stakeholder "cost estimate" migration "target architecture")

# Lowercase, and turn every run of punctuation into a single space, so "SLAs,"
# still matches "sla". Only the START of each word is anchored (a leading space),
# not the end, so "stakeholder" still matches inside the real plural
# "stakeholders" -- but "sla" cannot match starting midway through "translate",
# because that position in the haystack is never a word start.
normalize() {
  printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c '[:alnum:]/ ' ' ' | tr -s ' '
}

score() {
  local haystack=" $(normalize "$1")"
  shift
  local n=0
  local needle
  for word in "$@"; do
    needle=" $(normalize "$word")"
    case "$haystack" in
      *"$needle"*) n=$((n + 1)) ;;
    esac
  done
  echo "$n"
}

# An advert titled one thing, describing another -- deliberately, the same trick
# the AI Engineer course's first lesson pulls with a "Machine Learning Engineer" ad.
# It also contains "translate," a genuine trap for a careless substring match on "sla".
advert="Senior DevOps Engineer. You will define our target architecture across three
regions, produce cost estimates for migration, and work directly with stakeholders
to translate business requirements into a well-architected framework review."

cloud=$(score "$advert" "${SIGNALS_CLOUD[@]}")
devops=$(score "$advert" "${SIGNALS_DEVOPS[@]}")
sre=$(score "$advert" "${SIGNALS_SRE[@]}")
platform=$(score "$advert" "${SIGNALS_PLATFORM[@]}")
architect=$(score "$advert" "${SIGNALS_ARCHITECT[@]}")

echo "advert title says: DevOps Engineer"
printf '%-12s %s\n' "cloud:"     "$cloud"
printf '%-12s %s\n' "devops:"    "$devops"
printf '%-12s %s\n' "sre:"       "$sre"
printf '%-12s %s\n' "platform:"  "$platform"
printf '%-12s %s\n' "architect:" "$architect"

check "the title is misleading; the words say Solutions Architect" "$architect" "5"
check "and it scores above the title's own role" "$([ "$architect" -gt "$devops" ] && echo yes)" "yes"
check "the DevOps signal count is zero" "$devops" "0"
check "no false match of 'sla' inside 'translate'" "$sre" "0"

What it printed, when the build ran it

advert title says: DevOps Engineer
cloud:       1
devops:      0
sre:         0
platform:    0
architect:   5
ok   the title is misleading; the words say Solutions Architect
ok   and it scores above the title's own role
ok   the DevOps signal count is zero
ok   no false match of 'sla' inside 'translate'

On "years of experience" and long requirements lists. The same warning applies here as in every tech hiring market: a list of fifteen tools is a wish list written by committee, not quota. If you match the responsibilities and roughly half the tools, you are a real candidate for the role, whatever title is printed at the top of the posting.

What this course covers, and what it does not

  • Covered properly, and run for real: the shell, Git, containers, cloud fundamentals, AWS (compute, storage, serverless) against a free local stand-in, Terraform, CI/CD pipeline design, Kubernetes manifests, an observability stack, and security fundamentals -- the floor all five roles share.
  • Covered as concepts, with the real command shown: anywhere a step would need a paid account, a live cluster, or hardware this machine does not have (a second physical region, a GPU node, a company's actual production traffic), the panel says so on itself and is marked as shown, not run.
  • Not covered: a specific vendor's certification exam content, multi-cloud in depth (this course is AWS-first, since it is the market leader and the one with a viable free local emulator), and on-premises data centre hardware.

2 How to read a cloud/DevOps job advert

A cloud/DevOps advert is often a merged list from three different people: what the team uses today, what they used two years ago and have not fully retired, and what the hiring manager read about last week. Counting every listed tool as a hard requirement will talk you out of applying to jobs you can actually do.

An advert, scored two ways

Scoring yourself two ways

ran at build time
# A real-shaped advert: four responsibilities, five "required" tools, three "nice to have".
# One candidate. Score the same advert two different ways and compare.

RESP="deploy applications through a CI/CD pipeline;write Terraform to provision AWS infrastructure;monitor production systems and respond to incidents;automate repetitive operational tasks"
REQUIRED="aws;terraform;docker;linux;git"
NICE="kubernetes;golang;datadog"

CAN_DO="deploy applications through a CI/CD pipeline;write Terraform to provision AWS infrastructure;monitor production systems and respond to incidents;automate repetitive operational tasks"
KNOWS="terraform;docker;linux;git"

count_matches() {
  # args: candidate-list;items;target-list;items -- semicolon separated, count overlap
  local list1="$1" list2="$2"
  local n=0
  local IFS=';'
  local old_ifs="$IFS"
  read -ra items1 <<< "$list1"
  for item in "${items1[@]}"; do
    case ";$list2;" in
      *";$item;"*) n=$((n + 1)) ;;
    esac
  done
  IFS="$old_ifs"
  echo "$n"
}

count_total() {
  local IFS=';'
  read -ra items <<< "$1"
  echo "${#items[@]}"
}

resp_have=$(count_matches "$RESP" "$CAN_DO")
resp_total=$(count_total "$RESP")
req_have=$(count_matches "$REQUIRED" "$KNOWS")
req_total=$(count_total "$REQUIRED")
nice_have=$(count_matches "$NICE" "$KNOWS")
nice_total=$(count_total "$NICE")

# Naive score: every listed item (required + nice-to-have) counted equally.
naive_have=$((req_have + nice_have))
naive_total=$((req_total + nice_total))
naive_pct=$((naive_have * 100 / naive_total))

# Honest score: 70% the responsibilities you can actually do, 30% the required
# tools, nice-to-haves excluded entirely. Integer maths throughout (this is bash),
# rounded down, which only ever makes the honest score look more conservative.
resp_pct=$((resp_have * 100 / resp_total))
req_pct=$((req_have * 100 / req_total))
honest_pct=$(((resp_pct * 70 + req_pct * 30) / 100))

echo "naive  score: ${naive_pct}%   <- counts nice-to-haves as requirements"
echo "honest score: ${honest_pct}%   <- weights the actual work over the tool list"
echo
echo "missing required tools: kubernetes is NOT required, so nothing missing there"
echo "matched every responsibility: yes"

check "the naive score looks discouraging" "$([ "$naive_pct" -lt 70 ] && echo yes)" "yes"
check "the honest score says apply" "$([ "$honest_pct" -ge 85 ] && echo yes)" "yes"
check "all four responsibilities are met" "$resp_have" "$resp_total"
check "one required tool (aws) is the only real gap" "$((req_total - req_have))" "1"

What it printed, when the build ran it

naive  score: 50%   <- counts nice-to-haves as requirements
honest score: 94%   <- weights the actual work over the tool list

missing required tools: kubernetes is NOT required, so nothing missing there
matched every responsibility: yes
ok   the naive score looks discouraging
ok   the honest score says apply
ok   all four responsibilities are met
ok   one required tool (aws) is the only real gap

Notice what moved the score: not learning a new tool, but changing which numbers get weighted. The candidate above has never touched AWS specifically and still scores 90%+ honestly, because every responsibility is already a yes, and AWS knowledge transfers fast from any other cloud. A hiring manager filtering resumes by keyword would reject this candidate; one reading for responsibility match would not.

The tells that a listed tool is not really a hard requirement

  • It appears once, in a bullet list, with no elaboration elsewhere in the posting. A tool the team actually lives in gets mentioned in the "about the role" prose too, not only the bullet list.
  • It is a specific vendor product for a generic need. "Experience with Datadog" usually means "experience with an observability platform" -- Grafana, New Relic and CloudWatch experience all count, in practice.
  • The years-of-experience number is oddly specific ("6+ years Kubernetes" when Kubernetes itself is younger than that at some companies) -- a sign the number was copied from a template, not chosen deliberately.

What is not negotiable

Weighting responsibilities over tools is not the same as ignoring the posting. Three things in a cloud/DevOps advert are worth taking literally every time:

  • On-call expectations. If the posting mentions a rotation, ask about its frequency and page volume before accepting, not after.
  • "Which cloud." AWS, Azure and GCP overlap conceptually but not in their consoles, CLIs or specific service names. Transferable in weeks, but real enough to mention honestly if you have never used the specific one named.
  • Compliance-driven environments (healthcare, finance, government). These usually mean real constraints -- specific certifications, audit trails, change approval processes -- that are worth understanding before, not after, you start.

3 What counts as proof you can do the job

Nobody hires you because you deployed something once. They hire you because you showed evidence you can be trusted when it breaks -- and in this job, almost everything eventually breaks. Evidence has a specific shape, and a screenshot of a working app does not have it.

Why "I deployed an app to AWS" does not persuade anyone

The standard cloud/DevOps portfolio project is a small app, deployed to a cloud provider once, usually by clicking through a console. It fails to persuade for a specific reason: it proves you can follow a tutorial, not that you can operate a system under pressure. Nobody asks how it was deployed a second time, what happens when a dependency changes, or how you would find out it was down at 3am.

What an interviewer for this job is actually trying to find out:

  • Can you reproduce it? If the answer involves remembering which buttons you clicked, it is not infrastructure, it is a memory of infrastructure.
  • Do you know the blast radius? What breaks, and for whom, if this one thing goes down or is misconfigured.
  • Do you know how you would find out something is wrong? Before a customer tells you.
  • Can you undo it? A rollback plan that exists before the incident, not one invented during it.

A small project with Terraform (not console clicks), a documented rollback step, one alert that actually fires on a real condition, and a paragraph on what you would do if it paged you at 3am beats five console-deployed apps that each work once. Almost nobody writes the rollback plan or the 3am paragraph, which is exactly why doing so stands out.

Scoring a project against what actually counts

The same "score it two ways" trick as the previous lesson, aimed at portfolio projects instead of job adverts. A rubric with no framework needed: a checklist, and a way of counting.

Scoring a portfolio project against what actually counts

ran at build time
# A rubric for "is this project actually evidence," not a framework -- a checklist.
CRITERIA="provisioned_with_code;has_a_rollback_step;has_one_real_alert;documents_a_failure;cost_is_known"

# Project A: the common portfolio project. Clicked through the AWS console,
# took a screenshot, wrote "deployed to AWS!" in the README.
PROJECT_A=""

# Project B: smaller in scope, but every box below is genuinely true of it.
PROJECT_B="provisioned_with_code;has_a_rollback_step;documents_a_failure"

score() {
  local have="$1"
  local total=0 got=0
  local IFS=';'
  read -ra all <<< "$CRITERIA"
  for c in "${all[@]}"; do
    total=$((total + 1))
    case ";$have;" in
      *";$c;"*) got=$((got + 1)) ;;
    esac
  done
  echo "$got/$total"
}

score_a=$(score "$PROJECT_A")
score_b=$(score "$PROJECT_B")

echo "Project A (console-clicked, screenshot in README): $score_a"
echo "Project B (Terraform, rollback step, one honest failure writeup): $score_b"
echo
echo "Project A is not smaller in effort. It is missing the parts that were never about effort."

check "project A scores zero on the rubric that actually matters" "$score_a" "0/5"
check "project B clears more than half the rubric with less total work" "$score_b" "3/5"

What it printed, when the build ran it

Project A (console-clicked, screenshot in README): 0/5
Project B (Terraform, rollback step, one honest failure writeup): 3/5

Project A is not smaller in effort. It is missing the parts that were never about effort.
ok   project A scores zero on the rubric that actually matters
ok   project B clears more than half the rubric with less total work

The failure writeup is the highest-leverage part

Of everything on the rubric, "documents a failure" does the most work in an interview, for the same reason an evaluation set does in the AI Engineer course: almost nobody writes it down, so having one is unusually credible. It does not need to be dramatic. "I set the security group too wide, a scan found it within an hour, here is the diff that fixed it and the rule I added so it cannot happen again" is a better answer to "tell me about a time something went wrong" than a war story with no receipts.

What a real project's pipeline would include

A project that deploys itself on every push is a stronger version of the same evidence. The shape below is what that looks like as a real GitHub Actions workflow. It is printed here and not executed, because running it for real needs a GitHub repository, a runner and real cloud credentials -- none of which exist inside this course's build. Part 10 covers pipelines properly, with real syntax checked the way every other panel in this course is checked.

Shell

shown, not run
# What a real deploy pipeline looks like. NOT RUN HERE:
# a GitHub-hosted runner and real cloud credentials do not exist inside this build.
name: deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Terraform apply
        run: |
          terraform init
          terraform apply -auto-approve
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

This needs a real GitHub-hosted runner and real AWS credentials via secrets, neither of which this build has. Part 10 covers pipeline files in depth, with syntax and structure checked for real.

On certifications. An AWS or Kubernetes certification proves you sat an exam, not that you can operate the thing under real conditions. It is not worthless -- it is a reasonable signal that you know the vocabulary and the service catalogue -- but it is not a substitute for the rubric above, and interviewers who have done this job know the difference.

Newsletter

New writing, when it lands.

Occasional notes on shipping software with AI agents. No schedule, no filler, unsubscribe in one click.