<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shivam Singh</title>
    <description>The latest articles on DEV Community by Shivam Singh (@shivam_singh_8a9ada1e8b88).</description>
    <link>https://dev.to/shivam_singh_8a9ada1e8b88</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3988454%2Ffed3f67e-de33-4f06-ac8c-7a996ce948a1.jpg</url>
      <title>DEV Community: Shivam Singh</title>
      <link>https://dev.to/shivam_singh_8a9ada1e8b88</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shivam_singh_8a9ada1e8b88"/>
    <language>en</language>
    <item>
      <title>AWS SAA-C03: Containers, Serverless, DynamoDB &amp; Cognito — 10 Concepts Worth Knowing</title>
      <dc:creator>Shivam Singh</dc:creator>
      <pubDate>Thu, 02 Jul 2026 16:20:47 +0000</pubDate>
      <link>https://dev.to/shivam_singh_8a9ada1e8b88/aws-saa-c03-containers-serverless-dynamodb-cognito-10-concepts-worth-knowing-3opb</link>
      <guid>https://dev.to/shivam_singh_8a9ada1e8b88/aws-saa-c03-containers-serverless-dynamodb-cognito-10-concepts-worth-knowing-3opb</guid>
      <description>&lt;h2&gt;
  
  
  AWS SAA-C03: Containers, Serverless, DynamoDB &amp;amp; Cognito — 10 Concepts Worth Knowing
&lt;/h2&gt;

&lt;p&gt;This post covers the AWS container, serverless, and database services that appear heavily on the SAA-C03 exam — ECS, EKS, Fargate, Lambda, DynamoDB, API Gateway, Step Functions, and Cognito. For each one, the focus is on what the exam actually tests, not what the documentation says.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. ECS Launch Types — EC2 vs Fargate
&lt;/h2&gt;

&lt;p&gt;ECS (Elastic Container Service) runs Docker containers on AWS. It has two launch types with completely different operational models:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EC2 Launch Type:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You provision and manage the underlying EC2 instances&lt;/li&gt;
&lt;li&gt;The ECS agent runs on each instance and communicates with the ECS control plane&lt;/li&gt;
&lt;li&gt;AWS handles starting and stopping containers — you handle the instances they run on&lt;/li&gt;
&lt;li&gt;More control, more operational overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fargate Launch Type:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully serverless — no EC2 instances to provision, patch, or scale&lt;/li&gt;
&lt;li&gt;Define CPU and memory per task; AWS runs it&lt;/li&gt;
&lt;li&gt;Just create task definitions and Fargate handles the rest&lt;/li&gt;
&lt;li&gt;You pay per task, not per instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exam shortcut:&lt;/strong&gt; Any question containing "no infrastructure to manage," "serverless containers," or "don't want to manage EC2" → Fargate.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. ECS IAM Roles — Two Roles, Completely Different Jobs
&lt;/h2&gt;

&lt;p&gt;ECS uses two distinct IAM roles. Confusing them is one of the most common exam mistakes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EC2 Instance Profile&lt;/strong&gt; (EC2 Launch Type only):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attached to the EC2 instance, used by the ECS agent&lt;/li&gt;
&lt;li&gt;Lets the agent: pull images from ECR, send logs to CloudWatch, access Secrets Manager / Parameter Store&lt;/li&gt;
&lt;li&gt;This is infrastructure-level access — the host needs it to run containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ECS Task Role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attached to the task definition — your container's application code uses this&lt;/li&gt;
&lt;li&gt;Each service should have its own Task Role with only the permissions it needs&lt;/li&gt;
&lt;li&gt;Example: a container that reads from S3 needs an S3 read permission in its Task Role, not in the Instance Profile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The exam trap:&lt;/strong&gt; A question asks which role allows a container to read from S3. The answer is Task Role — not Instance Profile.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. EKS — Managed Kubernetes on AWS
&lt;/h2&gt;

&lt;p&gt;EKS (Elastic Kubernetes Service) runs managed Kubernetes clusters. The exam doesn't test Kubernetes internals — it tests when you'd choose EKS over ECS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose EKS when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The team already uses Kubernetes on-premises or on another cloud&lt;/li&gt;
&lt;li&gt;You need cloud-agnostic portability (Kubernetes works on Azure and GCP too)&lt;/li&gt;
&lt;li&gt;You're lifting and shifting an existing K8s workload to AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EKS node options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managed Node Groups: AWS creates and manages EC2 instances in an ASG&lt;/li&gt;
&lt;li&gt;Self-Managed Nodes: you create EC2s and register them to the cluster&lt;/li&gt;
&lt;li&gt;Fargate: no nodes at all — fully serverless&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EKS storage:&lt;/strong&gt; Uses a Container Storage Interface (CSI) driver. Supported: EBS (single-AZ), EFS (multi-AZ, works with Fargate), FSx for Lustre, FSx for NetApp ONTAP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exam shortcut:&lt;/strong&gt; "Company already uses Kubernetes" = EKS. "Starting fresh on AWS" = ECS.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Lambda — The Limits That Appear in Questions
&lt;/h2&gt;

&lt;p&gt;Lambda is a major exam topic and specific numbers appear in exam questions directly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;128 MB – 10 GB (1 MB increments)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max execution time&lt;/td&gt;
&lt;td&gt;900 seconds (15 minutes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tmp disk (/tmp)&lt;/td&gt;
&lt;td&gt;512 MB – 10 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;1,000 per region (increasable via support ticket)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment zip&lt;/td&gt;
&lt;td&gt;50 MB compressed / 250 MB uncompressed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment variables&lt;/td&gt;
&lt;td&gt;4 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key behaviours:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increasing RAM also scales CPU and network — they're linked&lt;/li&gt;
&lt;li&gt;Container images can be used as Lambda packages (must implement Lambda Runtime API)&lt;/li&gt;
&lt;li&gt;For arbitrary Docker images → ECS/Fargate, not Lambda&lt;/li&gt;
&lt;li&gt;The 15-minute limit is the most-used exam filter: any workload needing longer than 15 minutes eliminates Lambda as an option&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Lambda Concurrency — The Regional Throttling Trap
&lt;/h2&gt;

&lt;p&gt;Lambda concurrency is shared across all functions in a region. This creates a specific failure mode the exam tests:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;br&gt;
All Lambda functions in a region share 1,000 concurrent executions. If one high-traffic function consumes all 1,000, every other Lambda function in that region gets throttled — even completely unrelated ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Set Reserved Concurrency on each function to cap its maximum concurrency and protect the region's shared pool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throttle behaviour by invocation type:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Synchronous (API Gateway → Lambda): returns ThrottleError 429 immediately&lt;/li&gt;
&lt;li&gt;Asynchronous (S3 event → Lambda): retries automatically, then routes to Dead Letter Queue (DLQ)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Provisioned Concurrency:&lt;/strong&gt; Pre-warms Lambda execution environments to eliminate cold start latency. Lambda SnapStart (Java only) achieves the same by snapshotting the initialised environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Lambda at the Edge — CloudFront Functions vs Lambda@Edge
&lt;/h2&gt;

&lt;p&gt;When you need to run logic at CloudFront edge locations, AWS gives you two options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CloudFront Functions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript only&lt;/li&gt;
&lt;li&gt;Sub-millisecond startup, millions of requests per second&lt;/li&gt;
&lt;li&gt;Can only intercept Viewer Request and Viewer Response&lt;/li&gt;
&lt;li&gt;Native CloudFront feature — managed entirely within CloudFront&lt;/li&gt;
&lt;li&gt;Use for: URL rewrites, header manipulation, lightweight auth token checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lambda@Edge:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js or Python&lt;/li&gt;
&lt;li&gt;More compute time, heavier logic&lt;/li&gt;
&lt;li&gt;Intercepts all four CloudFront events: Viewer Request, Origin Request, Origin Response, Viewer Response&lt;/li&gt;
&lt;li&gt;Use for: complex routing, A/B testing, full authentication at the edge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lambda in VPC:&lt;/strong&gt; Lambda runs outside your VPC by default. To reach RDS, ElastiCache, or internal services, deploy Lambda into a VPC subnet with a security group. It uses an ENI to connect.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. DynamoDB Capacity Modes — Provisioned vs On-Demand
&lt;/h2&gt;

&lt;p&gt;DynamoDB is a fully managed NoSQL database. The exam tests capacity mode selection heavily:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provisioned Mode (default):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define Read Capacity Units (RCU) and Write Capacity Units (WCU) in advance&lt;/li&gt;
&lt;li&gt;Add auto-scaling to adjust based on CloudWatch metrics&lt;/li&gt;
&lt;li&gt;Cheaper per operation for stable, predictable traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;On-Demand Mode:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scales instantly with zero capacity planning&lt;/li&gt;
&lt;li&gt;Pay per request — more expensive per operation&lt;/li&gt;
&lt;li&gt;Right answer for: new applications with unknown traffic, workloads with steep sudden spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB core model:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tables made of Items, each with Attributes — flexible schema (attributes can differ per item)&lt;/li&gt;
&lt;li&gt;Primary Key: Partition Key alone, or Partition Key + Sort Key (composite)&lt;/li&gt;
&lt;li&gt;Max item size: 400 KB&lt;/li&gt;
&lt;li&gt;Data types: Scalar (String, Number, Binary, Boolean, Null), Document (List, Map), Sets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exam shortcut:&lt;/strong&gt; "Traffic is unpredictable / spike-heavy" = On-Demand. "Steady, cost-sensitive workload" = Provisioned with auto-scaling.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. DAX vs ElastiCache — Caching DynamoDB Reads
&lt;/h2&gt;

&lt;p&gt;Both are in-memory caches. The exam tests which to use for DynamoDB:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB Accelerator (DAX):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Purpose-built for DynamoDB — sits transparently in front of the table&lt;/li&gt;
&lt;li&gt;Microsecond read latency for cached items&lt;/li&gt;
&lt;li&gt;No application code changes — uses the same DynamoDB API&lt;/li&gt;
&lt;li&gt;Caches individual item lookups and query/scan results&lt;/li&gt;
&lt;li&gt;Default 5-minute TTL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ElastiCache (Redis or Memcached):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General-purpose cache&lt;/li&gt;
&lt;li&gt;Used to store aggregated or computed results (e.g. leaderboard totals, session data)&lt;/li&gt;
&lt;li&gt;Requires explicit cache logic in application code — populate, read, and invalidate manually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exam shortcut:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Cache DynamoDB reads with no code change" → DAX&lt;/li&gt;
&lt;li&gt;"Cache computed or aggregated results" → ElastiCache&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  9. DynamoDB Streams and Global Tables
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB Streams:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An ordered, time-stamped log of every item-level change (create, update, delete)&lt;/li&gt;
&lt;li&gt;24-hour retention&lt;/li&gt;
&lt;li&gt;Trigger Lambda functions in response — the standard serverless event-driven pattern&lt;/li&gt;
&lt;li&gt;Use cases: send notifications on new records, replicate to another table, real-time analytics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB Global Tables:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-region, active-active replication&lt;/li&gt;
&lt;li&gt;Applications can read AND write to the table in any configured region&lt;/li&gt;
&lt;li&gt;Changes replicate to all regions automatically&lt;/li&gt;
&lt;li&gt;Pre-requisite: DynamoDB Streams must be enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TTL (Time to Live):&lt;/strong&gt; Set an expiry timestamp attribute on any item. DynamoDB deletes expired items automatically at no cost. Use for: session tokens, temporary cache entries, regulatory retention limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exam trap — Global Tables vs RDS Multi-AZ:&lt;/strong&gt;&lt;br&gt;
RDS Multi-AZ = passive standby, sync replication, failover only — the standby cannot serve writes or reads. DynamoDB Global Tables = active-active, writes in any region. These are completely different patterns and the exam tests whether you know the distinction.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. API Gateway, Step Functions, and Cognito
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;API Gateway:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The managed front door for serverless APIs. Handles versioning, environments (dev/prod), auth, throttling, caching, and request/response transformation.&lt;/p&gt;

&lt;p&gt;Integration types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda: expose a REST API backed by Lambda — the most common serverless pattern&lt;/li&gt;
&lt;li&gt;HTTP Endpoint: proxy to an internal API or ALB&lt;/li&gt;
&lt;li&gt;AWS Service: directly expose SQS, Step Functions, Kinesis through the API layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Endpoint types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edge-Optimised (default): routes through CloudFront edge locations — for global clients&lt;/li&gt;
&lt;li&gt;Regional: for same-region clients&lt;/li&gt;
&lt;li&gt;Private: VPC-only access via a VPC endpoint (ENI)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security options: IAM Roles (internal apps), Cognito User Pools (external users), Lambda Authoriser (custom logic).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step Functions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Builds visual serverless workflows by orchestrating Lambda functions and AWS services. Supports sequence, parallel execution, conditions, timeouts, error handling, retries, and human approval steps.&lt;/p&gt;

&lt;p&gt;Integrates with Lambda, EC2, ECS, on-premises servers, API Gateway, and SQS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exam pattern:&lt;/strong&gt; "Orchestrate multiple Lambda functions" or "multi-step workflow with error handling" → Step Functions.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Amazon Cognito:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Handles user authentication so you don't build it yourself. Two distinct components with different jobs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cognito User Pools (CUP):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User directory: sign up, sign in, password reset, MFA, email/phone verification&lt;/li&gt;
&lt;li&gt;Supports federated login: Google, Facebook, SAML providers&lt;/li&gt;
&lt;li&gt;Returns a JWT token on successful authentication&lt;/li&gt;
&lt;li&gt;Integrates directly with API Gateway and ALB — they validate the JWT automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cognito Identity Pools (Federated Identity):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes a token (from User Pools or any identity provider) and exchanges it for temporary AWS credentials&lt;/li&gt;
&lt;li&gt;Users can then directly access AWS services: S3, DynamoDB, Lambda&lt;/li&gt;
&lt;li&gt;IAM policies defined in Cognito, customisable per user_id for fine-grained row-level access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Full pattern:&lt;/strong&gt;&lt;br&gt;
User logs in → User Pools → JWT → Identity Pools → temporary AWS credentials → direct S3/DynamoDB access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exam trigger phrase:&lt;/strong&gt; "hundreds of users," "mobile users," "federated identity," "authenticate with SAML" → Cognito, not IAM.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Through-Line
&lt;/h2&gt;

&lt;p&gt;Every service in this section does one thing: removes a layer of infrastructure management.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fargate removes EC2 from containers&lt;/li&gt;
&lt;li&gt;Lambda removes servers from compute&lt;/li&gt;
&lt;li&gt;DynamoDB removes database instance management&lt;/li&gt;
&lt;li&gt;API Gateway removes API infrastructure&lt;/li&gt;
&lt;li&gt;Cognito removes the need to build auth backends&lt;/li&gt;
&lt;li&gt;Step Functions removes the need to manage workflow state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exam tests whether you know which tool removes which layer — and what the limits and edge cases are for each one.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS SAA: 10 Core Concepts (Explained for Infrastructure Engineers)</title>
      <dc:creator>Shivam Singh</dc:creator>
      <pubDate>Wed, 17 Jun 2026 19:47:06 +0000</pubDate>
      <link>https://dev.to/shivam_singh_8a9ada1e8b88/aws-saa-10-core-concepts-explained-for-infrastructure-engineers-4p7</link>
      <guid>https://dev.to/shivam_singh_8a9ada1e8b88/aws-saa-10-core-concepts-explained-for-infrastructure-engineers-4p7</guid>
      <description>&lt;p&gt;This post covers what I learned while preparing for AWS_SAA — IAM, EC2, Load Balancing, Auto Scaling, RDS, Route 53, S3, CloudFront, and messaging services. I'm writing this for engineers who already understand infrastructure, so I'm skipping the basics and going straight to what the exam actually tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. IAM — It's About Least Privilege, Not Just Access
&lt;/h2&gt;

&lt;p&gt;IAM sounds simple until the exam starts testing edge cases. Here's what actually matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model:&lt;/strong&gt; Users → Groups → Policies. Never attach policies directly to individual users — always go through groups. It sounds obvious but exam questions test whether you know to put a user in a group vs attach a policy inline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roles over access keys:&lt;/strong&gt; When an EC2 instance or Lambda function needs to access AWS services, assign an IAM Role — never embed access keys in code or instance configuration. The exam will present scenarios where someone hardcodes credentials and ask you what's wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The security tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM Credentials Report (account-level) — lists all users and their credential status&lt;/li&gt;
&lt;li&gt;IAM Access Advisor (user-level) — shows which services a user has accessed, helps trim unused permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exam-critical rules:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root account is only for initial AWS account setup. Never use it for daily operations.&lt;/li&gt;
&lt;li&gt;One physical person = one IAM user. No sharing.&lt;/li&gt;
&lt;li&gt;MFA on root and privileged users is non-negotiable in exam scenarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The policy structure to memorise:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::my-bucket/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Effect, Action, Resource — the exam writes scenarios and you need to read policies and spot what's missing or wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. EC2 Purchasing Options — Architecture Starts Here
&lt;/h2&gt;

&lt;p&gt;The exam gives you a workload description and asks which purchasing option is correct. Memorise these:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;On-Demand&lt;/td&gt;
&lt;td&gt;Unpredictable, spiky, short-term workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reserved (1–3 yr)&lt;/td&gt;
&lt;td&gt;Steady-state production workloads — biggest discount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Savings Plans&lt;/td&gt;
&lt;td&gt;Flexible Reserved — commit to $/hour not instance type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spot&lt;/td&gt;
&lt;td&gt;Fault-tolerant batch jobs, can be interrupted with 2-min notice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dedicated Host&lt;/td&gt;
&lt;td&gt;Compliance requirements, per-socket/per-core software licensing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dedicated Instance&lt;/td&gt;
&lt;td&gt;Isolated hardware, but AWS still manages the host&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The exam trap:&lt;/strong&gt; Spot instances are the cheapest but can be interrupted. Any question about a database, stateful app, or anything that can't tolerate interruption → eliminate Spot immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EC2 Hibernate&lt;/strong&gt; is a frequent exam distractor — it preserves RAM state to EBS so the instance restarts faster. The root EBS volume must be encrypted. Use case: services that take a long time to warm up.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. EC2 Storage — EBS vs EFS vs Instance Store
&lt;/h2&gt;

&lt;p&gt;Three storage types, three completely different behaviours:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EBS (Elastic Block Store):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attached to one EC2 instance at a time (except io1/io2 Multi-Attach)&lt;/li&gt;
&lt;li&gt;Persists when you stop the instance&lt;/li&gt;
&lt;li&gt;AZ-specific — to move across AZs, take a snapshot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EFS (Elastic File System):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network file system — can be mounted by hundreds of EC2 instances simultaneously&lt;/li&gt;
&lt;li&gt;Works across multiple AZs&lt;/li&gt;
&lt;li&gt;Auto-scales, you pay for what you use&lt;/li&gt;
&lt;li&gt;Use case: shared content, CMS, web serving across multiple instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Instance Store:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physically attached to the host — highest possible IOPS&lt;/li&gt;
&lt;li&gt;Completely lost when the instance stops or terminates&lt;/li&gt;
&lt;li&gt;Use case: temporary buffers, caches, scratch data only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The exam shortcut:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Shared across multiple EC2 instances" → &lt;strong&gt;EFS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;"Highest IOPS, temporary data" → &lt;strong&gt;Instance Store&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;"Persistent, single-instance" → &lt;strong&gt;EBS&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Elastic Load Balancing — Three Types, Different Use Cases
&lt;/h2&gt;

&lt;p&gt;AWS has three load balancer types and the exam tests whether you know which fits which scenario:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application Load Balancer (ALB) — Layer 7:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routes based on URL path (&lt;code&gt;/api&lt;/code&gt; → one target group, &lt;code&gt;/web&lt;/code&gt; → another)&lt;/li&gt;
&lt;li&gt;Routes based on hostname (&lt;code&gt;api.example.com&lt;/code&gt; vs &lt;code&gt;app.example.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Routes based on query strings and headers&lt;/li&gt;
&lt;li&gt;Native support for microservices and containers (ECS integration)&lt;/li&gt;
&lt;li&gt;Best for HTTP/HTTPS applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Network Load Balancer (NLB) — Layer 4:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles millions of requests per second with ultra-low latency&lt;/li&gt;
&lt;li&gt;Has a static IP per AZ — important for whitelisting&lt;/li&gt;
&lt;li&gt;Supports TCP, UDP, TLS&lt;/li&gt;
&lt;li&gt;Use when you need extreme performance or a fixed IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Classic Load Balancer (CLB) — Legacy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't design new architectures with it&lt;/li&gt;
&lt;li&gt;The exam mentions it but the answer is rarely CLB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exam pattern:&lt;/strong&gt; The question describes a routing requirement — you pick the load balancer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Route by URL path" → ALB&lt;/li&gt;
&lt;li&gt;"Static IP for firewall rules" → NLB&lt;/li&gt;
&lt;li&gt;"Microservices on ECS" → ALB&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Auto Scaling Groups — Four Scaling Policies
&lt;/h2&gt;

&lt;p&gt;ASG lets EC2 scale automatically. The exam tests which scaling policy matches which requirement:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target Tracking:&lt;/strong&gt; Simplest. "Keep average CPU at 40%." AWS handles everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step Scaling:&lt;/strong&gt; "Add 2 instances when CPU &amp;gt; 70%. Remove 1 when CPU &amp;lt; 30%." You define the steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scheduled Scaling:&lt;/strong&gt; "Increase min capacity to 10 every Friday at 5pm." You know the traffic pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Predictive Scaling:&lt;/strong&gt; Machine learning-based. Analyses historical load and pre-scales before the spike hits. Best for recurring patterns you can't manually schedule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cooldown period:&lt;/strong&gt; After a scaling activity, ASG waits before scaling again (default 300 seconds). The exam tests this — if instances are launching and terminating in a loop, suspect the cooldown is too short.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. RDS — Read Replicas vs Multi-AZ (Highest Exam Yield)
&lt;/h2&gt;

&lt;p&gt;This is tested in almost every exam. These two features sound similar and are completely different:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read Replicas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Purpose: &lt;strong&gt;read scaling&lt;/strong&gt; — offload SELECT queries from the primary&lt;/li&gt;
&lt;li&gt;Replication: &lt;strong&gt;asynchronous&lt;/strong&gt; — slight lag possible&lt;/li&gt;
&lt;li&gt;Scope: same AZ, cross-AZ, or cross-region&lt;/li&gt;
&lt;li&gt;Network cost: free within same region, charged cross-region&lt;/li&gt;
&lt;li&gt;You connect to them explicitly — your app must be updated to use the replica endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Multi-AZ:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Purpose: &lt;strong&gt;disaster recovery&lt;/strong&gt; — automatic failover&lt;/li&gt;
&lt;li&gt;Replication: &lt;strong&gt;synchronous&lt;/strong&gt; — always up to date&lt;/li&gt;
&lt;li&gt;Scope: same region, different AZ&lt;/li&gt;
&lt;li&gt;Failover: DNS automatically points to standby — no app change needed&lt;/li&gt;
&lt;li&gt;The standby instance cannot serve read traffic — it exists only for failover&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Aurora specifics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Up to 15 Read Replicas (vs 5 for standard RDS)&lt;/li&gt;
&lt;li&gt;Replication lag under 10ms&lt;/li&gt;
&lt;li&gt;Aurora Global Database: primary region + up to 10 secondary regions, cross-region replication under 1 second&lt;/li&gt;
&lt;li&gt;Aurora Serverless: auto-scales compute — good for infrequent/unpredictable workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ElastiCache — the exam trap:&lt;/strong&gt; ElastiCache is an in-memory cache (Redis or Memcached). The exam often presents a scenario where an RDS database is under heavy read load — the correct answer is frequently "add ElastiCache" rather than more Read Replicas, because cached data never hits the DB at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Route 53 Routing Policies — Memorise All Six
&lt;/h2&gt;

&lt;p&gt;Route 53 routing policies are one of the highest-density exam topics. Six policies, each with a distinct use case:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple:&lt;/strong&gt; One record, one resource. No health checks. Use for a single server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weighted:&lt;/strong&gt; Split traffic by percentage. Use for A/B testing or canary deployments. Example: 90% to v1, 10% to v2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency:&lt;/strong&gt; Routes to the region with the lowest latency for the user. AWS measures latency, not geographic distance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failover:&lt;/strong&gt; Active/passive disaster recovery. Primary gets traffic; secondary gets traffic only if primary fails the health check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Geolocation:&lt;/strong&gt; Routes based on the user's actual location (country, continent). Use for content localisation or legal data residency requirements. Different from Latency — it's about location, not speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multivalue Answer:&lt;/strong&gt; Returns up to 8 healthy records randomly. Not a replacement for a load balancer but adds basic client-side load distribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TTL matters:&lt;/strong&gt; High TTL = less Route 53 traffic, but DNS changes take longer to propagate. Low TTL = faster propagation, more Route 53 queries (costs more). The exam tests this trade-off.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. S3 — Storage Classes and When to Use Each
&lt;/h2&gt;

&lt;p&gt;S3 has seven storage classes. The exam presents a scenario and asks which class is correct:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Availability&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;99.99%&lt;/td&gt;
&lt;td&gt;Frequently accessed data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard-IA&lt;/td&gt;
&lt;td&gt;99.9%&lt;/td&gt;
&lt;td&gt;Infrequent access, rapid retrieval — DR, backups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One Zone-IA&lt;/td&gt;
&lt;td&gt;99.5%&lt;/td&gt;
&lt;td&gt;Single AZ only, cheaper — recreatable data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glacier Instant Retrieval&lt;/td&gt;
&lt;td&gt;99.9%&lt;/td&gt;
&lt;td&gt;Archive with millisecond retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glacier Flexible&lt;/td&gt;
&lt;td&gt;99.99%&lt;/td&gt;
&lt;td&gt;Archive — minutes to hours retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glacier Deep Archive&lt;/td&gt;
&lt;td&gt;99.99%&lt;/td&gt;
&lt;td&gt;Lowest cost — 12+ hour retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intelligent-Tiering&lt;/td&gt;
&lt;td&gt;99.9%&lt;/td&gt;
&lt;td&gt;Unknown or changing access patterns — auto-moves&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Lifecycle Rules:&lt;/strong&gt; Automatically transition objects between classes based on age. Example: Standard → Standard-IA after 30 days → Glacier after 90 days. The exam tests whether you know which transitions are valid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Replication:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CRR (Cross-Region Replication): compliance, lower latency for global users&lt;/li&gt;
&lt;li&gt;SRR (Same-Region Replication): log aggregation, live replication between prod and test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Versioning must be enabled on both source and destination buckets for replication to work.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. S3 Security — Four Encryption Methods
&lt;/h2&gt;

&lt;p&gt;S3 has four server-side encryption options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSE-S3:&lt;/strong&gt; AWS manages keys entirely. Default for new buckets. Header: &lt;code&gt;x-amz-server-side-encryption: AES256&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSE-KMS:&lt;/strong&gt; You use AWS KMS. You control the key rotation and audit trail via CloudTrail. Header: &lt;code&gt;x-amz-server-side-encryption: aws:kms&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSE-C:&lt;/strong&gt; You provide your own key with every request. AWS doesn't store the key. HTTPS required. Use when you must control key material.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client-Side Encryption:&lt;/strong&gt; You encrypt before uploading. AWS never sees plaintext. Use for strictest compliance requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bucket Policies vs IAM Policies:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bucket policies are resource-based — they're attached to the bucket&lt;/li&gt;
&lt;li&gt;IAM policies are identity-based — attached to users/roles&lt;/li&gt;
&lt;li&gt;Both can allow or deny access. Explicit DENY anywhere always wins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The CORS trap:&lt;/strong&gt; If your JavaScript app at &lt;code&gt;example.com&lt;/code&gt; calls an S3 bucket, you must configure CORS on the bucket. The exam describes a browser error and asks what's wrong — the answer is CORS.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. SQS vs SNS vs Kinesis — Decouple vs Stream
&lt;/h2&gt;

&lt;p&gt;Three services, three different problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQS (Simple Queue Service):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consumers pull messages&lt;/li&gt;
&lt;li&gt;Message deleted after consumed&lt;/li&gt;
&lt;li&gt;At-least-once delivery (can get duplicates in Standard queue)&lt;/li&gt;
&lt;li&gt;FIFO queue: exactly-once, ordered, 300 TPS&lt;/li&gt;
&lt;li&gt;Default retention: 4 days, max 14 days&lt;/li&gt;
&lt;li&gt;Use for: decoupling microservices, async processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SNS (Simple Notification Service):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publisher pushes, all subscribers receive&lt;/li&gt;
&lt;li&gt;Messages not persisted — lost if not delivered&lt;/li&gt;
&lt;li&gt;Up to 12.5 million subscribers per topic&lt;/li&gt;
&lt;li&gt;Use for: fanout notifications, triggering multiple downstream actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Kinesis Data Streams:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time streaming at high throughput&lt;/li&gt;
&lt;li&gt;Data retained up to 365 days — you can replay&lt;/li&gt;
&lt;li&gt;Ordering guaranteed per shard&lt;/li&gt;
&lt;li&gt;Use for: real-time analytics, log ingestion, clickstream data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The fan-out pattern (exam favourite):&lt;/strong&gt;&lt;br&gt;
One event → SNS Topic → multiple SQS queues (one per downstream service). This is the go-to pattern when one event needs to trigger multiple independent consumers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon MQ:&lt;/strong&gt; When migrating existing on-premises apps that use MQTT, AMQP, or STOMP protocols, use Amazon MQ rather than rewriting for SQS/SNS. The exam presents legacy migration scenarios specifically to test this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Classic Architecture Patterns — The Mental Model
&lt;/h2&gt;

&lt;p&gt;Every SAA exam scenario is a variation of five building blocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stateless app&lt;/strong&gt; → don't store sessions in EC2 — use ElastiCache (Redis) or DynamoDB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared file storage&lt;/strong&gt; → EFS, not EBS (EBS is single-instance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static content&lt;/strong&gt; → S3 + CloudFront (never serve static files from EC2)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-heavy database&lt;/strong&gt; → RDS Read Replica or ElastiCache in front&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-write database&lt;/strong&gt; → Aurora (faster writes, auto-scales storage)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you see a complex architecture question, strip it back to these five patterns. The correct answer almost always combines two or three of them.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloud</category>
      <category>certification</category>
    </item>
    <item>
      <title>8 Python Patterns a DevOps Engineer Actually Needs for ML</title>
      <dc:creator>Shivam Singh</dc:creator>
      <pubDate>Wed, 17 Jun 2026 07:52:24 +0000</pubDate>
      <link>https://dev.to/shivam_singh_8a9ada1e8b88/8-python-patterns-a-devops-engineer-actually-needs-for-ml-39c6</link>
      <guid>https://dev.to/shivam_singh_8a9ada1e8b88/8-python-patterns-a-devops-engineer-actually-needs-for-ml-39c6</guid>
      <description>&lt;p&gt;I'm a DevOps engineer moving into MLOps.&lt;/p&gt;

&lt;p&gt;When I started, I assumed my Python scripting background had me covered. I already wrote automation scripts, parsed YAML, called APIs, handled errors. Python is Python, right?&lt;/p&gt;

&lt;p&gt;Wrong.&lt;/p&gt;

&lt;p&gt;DevOps Python and ML Python look different, feel different, and break in different ways. After completing a focused Python-for-ML course, here are the 8 patterns that genuinely shifted how I work — written specifically for engineers with a DevOps background.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. DataFrames over loops
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; process data with &lt;code&gt;for&lt;/code&gt; loops and lists of dicts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; everything lives in a pandas DataFrame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The DevOps way
&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]:&lt;/span&gt;  &lt;span class="c1"&gt;# skip header
&lt;/span&gt;        &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])})&lt;/span&gt;

&lt;span class="n"&gt;high_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# The ML way
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;high_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# done in one line
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Every ML library — sklearn, MLflow, XGBoost — expects DataFrames as input. Keep working with lists of dicts and you'll spend half your time converting between formats. Learn the DataFrame mental model first; everything else follows.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. NumPy arrays for numerical work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; use Python lists. Flexible, familiar, fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; uses NumPy arrays for anything numerical.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The DevOps way
&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# loop over each element
&lt;/span&gt;&lt;span class="n"&gt;squared&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# another loop
&lt;/span&gt;
&lt;span class="c1"&gt;# The ML way
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;    &lt;span class="c1"&gt;# vectorized — no loop
&lt;/span&gt;&lt;span class="n"&gt;squared&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;   &lt;span class="c1"&gt;# vectorized — no loop
&lt;/span&gt;
&lt;span class="c1"&gt;# Operations across entire arrays in one line
&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;std&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; ML regularly works with 10 million data points. At that scale, a Python loop takes minutes; a vectorized NumPy operation takes seconds. The performance difference isn't academic — it determines whether your data pipeline is usable or not.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Dataclasses for config management
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; YAML files parsed to Python dicts. You know this pattern deeply — Kubernetes, Ansible, Docker Compose all run on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; hyperparameters and model configs live in Python dataclasses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The DevOps way — YAML parsed to a dict
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config.yaml&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_rate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# no type checking
&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;trainign&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;batch_size&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# typo fails at runtime, not at write time
&lt;/span&gt;
&lt;span class="c1"&gt;# The ML way — dataclass with types and defaults
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt;
    &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nb"&gt;int&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;
    &lt;span class="n"&gt;epochs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="nb"&gt;int&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nb"&gt;str&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;random_forest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="nb"&gt;int&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ModelConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;epochs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# IDE autocomplete works
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="c1"&gt;# ModelConfig(learning_rate=0.01, batch_size=32, ...)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; ML experiments involve running the same model with hundreds of different hyperparameter combinations. Dataclasses give you autocomplete, type checking (typos caught at write time, not runtime), and they're naturally printable and comparable. They're not a replacement for YAML — they work alongside it for the Python-layer config.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Context managers beyond file handling
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; you already know &lt;code&gt;with open()&lt;/code&gt;. You understand context managers in principle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; extends this same pattern into experiment tracking, GPU management, and timing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# You already know this
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config.yaml&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ML extends the exact same pattern
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;

&lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;housing-price-prediction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;random_forest_v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_params&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n_estimators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_depth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;learning_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# ... train your model here ...
&lt;/span&gt;
    &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_metrics&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train_accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val_accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mf"&gt;0.91&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rmse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;           &lt;span class="mi"&gt;42300&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sklearn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# run auto-closes and saves everything, even on exception
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; You already understand the mental model — the context manager guarantees cleanup. In ML, this pattern appears constantly: experiment tracking, PyTorch GPU contexts, timed blocks, feature store connections. Recognising it means you're never starting from zero.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Pathlib over os.path
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; &lt;code&gt;os.path.join()&lt;/code&gt;, &lt;code&gt;os.makedirs()&lt;/code&gt;, &lt;code&gt;os.path.exists()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; uses &lt;code&gt;pathlib.Path&lt;/code&gt; — the modern Python standard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# The DevOps way
&lt;/span&gt;&lt;span class="n"&gt;data_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;raw&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;experiment_1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# The ML way
&lt;/span&gt;&lt;span class="n"&gt;data_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;raw&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;model_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;experiment_1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;model_dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Bonus: globbing is clean
&lt;/span&gt;&lt;span class="n"&gt;all_csvs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/*.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Every ML codebase and library you'll read or contribute to uses pathlib — sklearn, MLflow, PyTorch DataLoaders. Open a PR using &lt;code&gt;os.path&lt;/code&gt; and reviewers will ask you to change it. Better to build the habit now.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Type hints are not optional in ML
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; skip type hints on scripts. Scripts are short-lived and single-purpose — they run, they work, they're done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; enforces type hints because ML code goes to production and lives for years.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# DevOps script — no type hints, fine for a one-off
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="c1"&gt;# ML production code — type hints are documentation
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;preprocess_features&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target_column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;drop_nulls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nb"&gt;bool&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Separate features and target, apply threshold filter.

    Args:
        df:            Raw input DataFrame from data pipeline.
        target_column: Name of the label column to predict.
        threshold:     Minimum value filter for numerical features.
        drop_nulls:    Whether to drop rows with missing values.

    Returns:
        (X, y) tuple ready for sklearn fit().
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;drop_nulls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dropna&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;target_column&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;target_column&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; You'll write a preprocessing function today. Someone else — or future you — will call it six months later while debugging a production model. Type hints and docstrings are the difference between "self-explanatory" and "I have to read the entire function to understand what it expects." In ML, functions get reused across experiments for months.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Generators for large datasets
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; load files into memory. For config files and log snippets, this is perfectly fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; uses generators to stream large data without running out of RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# DevOps way — loads everything into memory
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_all_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# entire file in RAM — fine at 10MB, fatal at 50GB
&lt;/span&gt;
&lt;span class="c1"&gt;# ML way — generator, one line at a time
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# constant memory regardless of file size
&lt;/span&gt;
&lt;span class="c1"&gt;# The ML pattern you'll use most: batch generator for training
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;batch_generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Yields (X_batch, y_batch) pairs without loading all data at once.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;batch_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;batch_idx&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;batch_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;X_batch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_batch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;batch_generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;partial_fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_batch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; ML training datasets are routinely 10GB–1TB. You cannot &lt;code&gt;pd.read_csv()&lt;/code&gt; a 100GB file. Once you understand &lt;code&gt;yield&lt;/code&gt;, you'll recognise the same pattern in PyTorch DataLoaders, TensorFlow &lt;code&gt;tf.data&lt;/code&gt; pipelines, and every production data pipeline you'll ever build.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Experiment logging, not print statements
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What DevOps engineers do:&lt;/strong&gt; &lt;code&gt;print()&lt;/code&gt; for debugging, &lt;code&gt;logging&lt;/code&gt; for production scripts. You log events and errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ML code does:&lt;/strong&gt; tracks parameters, metrics, and artifacts across &lt;em&gt;every&lt;/em&gt; run so you can compare them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The DevOps approach
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accuracy: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;accuracy&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# lost when terminal closes
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RMSE: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rmse&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model trained. Accuracy: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;accuracy&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# better, but still siloed
&lt;/span&gt;
&lt;span class="c1"&gt;# The ML approach — tracked, comparable, reproducible
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;

&lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;california-housing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xgboost_depth5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_params&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XGBoost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_depth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n_estimators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;learning_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feature_set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v2_with_distance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# ... train your model ...
&lt;/span&gt;
    &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_metrics&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train_rmse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;38400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val_rmse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;42300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train_r2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mf"&gt;0.96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val_r2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="mf"&gt;0.94&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sklearn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# mlflow ui
# Now compare run 1 vs run 47 side-by-side — every param and metric tracked
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; You'll run the same model with 50 different parameter combinations trying to improve accuracy. Without tracking, by run 20 you'll have no idea which config produced which result. MLflow runs locally or in the cloud — it's the DevOps-familiar way into experiment tracking, built on tools you already recognise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mindset shift in one sentence
&lt;/h2&gt;

&lt;p&gt;DevOps Python keeps systems running. ML Python transforms data and trains models.&lt;/p&gt;

&lt;p&gt;Same language — different idioms, different patterns, different instincts.&lt;/p&gt;

&lt;p&gt;The context manager you already know is the same pattern powering experiment tracking. The YAML config you know is the same concept as a dataclass. The log file you monitor is the same idea as an experiment run.&lt;/p&gt;

&lt;p&gt;The bridge is shorter than it looks. You just need to know where it starts.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
