DEV Community

Darffy D.K.
Darffy D.K.

Posted on

I Automated My Workflow with Devops Practices - Here is How

I Automated My Workflow with DevOps Practices - Here's How I Did It

Look, I'll be honest with you – a year ago, I was that guy manually deploying code, crossing my fingers, and hoping nothing broke in production. I'd push changes, manually SSH into servers, run a few commands, and pray. It was chaotic, error-prone, and absolutely soul-crushing when something went wrong at 2 AM on a Sunday.

Then I realized: I'm literally a developer. Why am I doing manual work a script could handle?

So I decided to actually learn DevOps. Not the "I'm becoming a DevOps engineer" kind of learning, but rather adopting DevOps practices to make my own development life infinitely better. This isn't a story about becoming ops-savvy – it's about how automation changed everything for me, and I want to walk you through exactly what I did.

The Breaking Point

I was working on a mid-sized e-commerce platform with a small team. Every Friday, we'd have a release. And every Friday, I'd spend 2-3 hours doing repetitive tasks:

  • Building the Docker image manually
  • Pushing it to a registry
  • Logging into AWS
  • Updating the ECS task definition
  • Restarting the services
  • Running smoke tests
  • Checking logs for errors

One Friday, I accidentally deployed the wrong image to production. Wrong. Image. The whole morning went away fixing it. That's when my brain finally clicked: This is stupid. Automate it.

Step 1: Containerize Everything (Docker)

First things first – I needed consistent environments. I started with Docker because, honestly, it's kind of become table stakes for developers now.

My Dockerfile went from non-existent to this:

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

RUN npm run build

EXPOSE 3000

CMD ["node", "dist/index.js"]
Enter fullscreen mode Exit fullscreen mode

I'm not going to lie – my first few Dockerfiles were trash. I didn't understand layers, caching, or multi-stage builds. I just copied the entire project and called it a day. But you know what? I learned by doing.

The key thing here was realizing that Docker gave me reproducibility. What runs on my machine runs in production. Wild concept, I know.

Step 2: Set Up CI/CD with GitHub Actions

This is where the magic started happening. I didn't want to use Jenkins (seemed overkill) or pay for fancy CI/CD platforms. GitHub Actions is built right into GitHub and it's genuinely excellent.

Here's my workflow file that changed everything:

name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build Docker image
        run: docker build -t my-app:${{ github.sha }} .

      - name: Push to ECR
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: |
          aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.ECR_REGISTRY }}
          docker tag my-app:${{ github.sha }} ${{ secrets.ECR_REGISTRY }}/my-app:latest
          docker push ${{ secrets.ECR_REGISTRY }}/my-app:latest

      - name: Update ECS service
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: |
          aws ecs update-service \
            --cluster production-cluster \
            --service my-app-service \
            --force-new-deployment \
            --region us-east-1

      - name: Notify Slack
        if: success()
        run: |
          curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
            -H 'Content-Type: application/json' \
            -d '{"text":"Deployment to production successful! 🚀"}'
Enter fullscreen mode Exit fullscreen mode

Yeah, that looks intimidating. I know because I stared at it confused for about 4 hours. But breaking it down:

  1. When does it run? Every time I push to main
  2. What does it do? Check out code → Install deps → Run tests → Build Docker image → Push to ECR → Update ECS → Notify Slack

The beautiful part? Now I just git push, grab coffee, and the whole pipeline runs automatically. No manual intervention.

Step 3: Infrastructure as Code with Terraform

Next up was Terraform. My AWS infrastructure used to be a mess of clickety-clacks in the console. "Was that security group created by me or someone else?" Nobody knows.

Terraform let me define everything in code:

resource "aws_ecs_cluster" "main" {
  name = "production-cluster"
}

resource "aws_ecs_task_definition" "app" {
  family                   = "my-app"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"
  memory                   = "512"

  container_definitions = jsonencode([{
    name      = "my-app"
    image     = "${aws_ecr_repository.app.repository_url}:latest"
    essential = true
    portMappings = [{
      containerPort = 3000
      protocol      = "tcp"
    }]
  }])
}
Enter fullscreen mode Exit fullscreen mode

Now infrastructure changes go through git like code changes. Pull requests for infrastructure. Code review for your AWS setup. It's chef's kiss.

The learning curve here was real though. I definitely destroyed a production database once (didn't have protection enabled, my bad). But that's actually what made it valuable – I learned to respect infrastructure management.

Step 4: Monitoring and Alerting

Automating deployment is cool, but you know what's cooler? Knowing immediately when something breaks.

I set up Datadog (okay, slight budget on this one, but there are free alternatives like Prometheus) to monitor everything. Health checks, error rates, response times, database connections – all of it.

The beauty is that now I get alerted before users even notice something's wrong. I have a Slack integration that pings me with actionable information:

🚨 High Error Rate Detected
Service: my-app
Error Rate: 15% (threshold: 5%)
Duration: 5 minutes
Affected Endpoints: /api/checkout
Enter fullscreen mode Exit fullscreen mode

Instead of waking up to angry customer emails, I wake up to a Slack notification, fix the issue, and redeploy. Takes me 20 minutes instead of 3 hours of chaos.

The Honest Truth

This didn't happen overnight, and it definitely wasn't friction-free. Here's what actually went down:

What Was Hard:

  • Understanding GitHub Actions syntax (YAML is finicky, man)
  • Getting AWS permissions right (IAM is a nightmare, not gonna sugarcoat it)
  • First time I accidentally deleted the database with Terraform (yikes)
  • Debugging why deployments were silently failing

What I Wish I'd Known Earlier:

  • Start with GitHub Actions, not Jenkins
  • Use managed services (ECS, RDS) instead of managing servers yourself
  • Invest in monitoring from day one
  • Terraform takes time to understand, but it's worth it

The Results

After six months of having this pipeline in place, here's what changed:

  • Deployment time: 3 hours → 10 minutes (mostly waiting for tests to run)
  • Failures in production: Multiple per month → Maybe one every few months
  • Stress level: Genuinely lower. Deployments don't stress me anymore
  • **Time

Disclosure: This article contains affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you.

📚 Want to learn more? Check out these top resources on Amazon.

Top comments (0)