Wednesday, 26 February 2025

Understanding AWS Well-Architected Framework: A Deep Dive

 

Introduction

Amazon Web Services (AWS) has revolutionized cloud computing by providing scalable, secure, and cost-effective solutions for businesses. To ensure that organizations build secure, high-performing, resilient, and efficient infrastructure, AWS introduced the Well-Architected Framework. This framework provides best practices and guidelines to help architects create robust cloud-based solutions.

In this article, we will take a deep dive into the AWS Well-Architected Framework, exploring its core pillars, benefits, and how organizations can implement it effectively.

What is the AWS Well-Architected Framework?

The AWS Well-Architected Framework is a set of best practices designed to help organizations assess and improve their cloud architecture. It is based on six pillars:

  1. Operational Excellence
  2. Security
  3. Reliability
  4. Performance Efficiency
  5. Cost Optimization
  6. Sustainability

Each pillar focuses on specific aspects of cloud architecture, ensuring that businesses can build applications that are scalable, resilient, and cost-effective.

The Six Pillars of AWS Well-Architected Framework

1. Operational Excellence

Operational excellence focuses on running workloads efficiently, continuously improving processes, and ensuring operational insights. Best practices include:

  • Automating processes with Infrastructure as Code (IaC)
  • Monitoring applications with AWS CloudWatch
  • Implementing deployment strategies such as blue/green deployments
  • Enforcing policies using AWS Config and AWS Systems Manager

2. Security

Security is essential for protecting data, applications, and infrastructure. Key security best practices include:

  • Using identity and access management (IAM) roles and policies
  • Encrypting data in transit and at rest with AWS KMS
  • Implementing multi-factor authentication (MFA)
  • Conducting regular security assessments with AWS Security Hub

3. Reliability

Reliability ensures that workloads recover from failures and dynamically adjust to changes. Key principles include:

  • Implementing auto-scaling using AWS Auto Scaling
  • Distributing workloads with AWS Elastic Load Balancing (ELB)
  • Designing for fault tolerance using AWS Availability Zones (AZs) and Regions
  • Regularly testing disaster recovery strategies

4. Performance Efficiency

Performance efficiency focuses on using computing resources effectively to meet system requirements. Best practices include:

  • Selecting the right AWS services for workloads (e.g., EC2, Lambda, or Fargate)
  • Optimizing storage solutions using Amazon S3, EFS, or DynamoDB
  • Leveraging caching with Amazon CloudFront and AWS ElastiCache
  • Monitoring performance metrics and making data-driven decisions

5. Cost Optimization

Cost optimization helps organizations minimize expenses while maximizing business value. Key strategies include:

  • Right-sizing instances using AWS Compute Optimizer
  • Using Reserved Instances and Spot Instances for Cost Savings
  • Implementing auto-scaling to reduce resource wastage
  • Monitoring cost and usage with AWS Cost Explorer

6. Sustainability

Sustainability is the latest addition to the Well-Architected Framework, focusing on reducing environmental impact. Best practices include:

  • Optimizing workloads to consume fewer resources
  • Using energy-efficient AWS Regions
  • Leveraging AWS Graviton-based instances for better efficiency
  • Implementing carbon footprint monitoring tools

Benefits of the AWS Well-Architected Framework

Implementing the AWS Well-Architected Framework provides several advantages:

  • Improved Security: Enhanced security practices protect sensitive data.
  • Better Resilience: Applications become more fault-tolerant and highly available.
  • Optimized Costs: Businesses reduce operational expenses and maximize cost efficiency.
  • Increased Performance: Applications perform better under varying loads.
  • Scalability: Workloads automatically adjust based on demand.

How to Implement the AWS Well-Architected Framework

Step 1: Perform a Well-Architected Review

AWS provides the AWS Well-Architected Tool, which helps organizations evaluate their cloud architecture based on the six pillars. Businesses can identify areas for improvement and take corrective actions.

Step 2: Adopt Best Practices

Implement the recommended best practices for each pillar to improve security, performance, and cost-efficiency.

Step 3: Automate and Optimize

Automation tools such as AWS CloudFormation, AWS Lambda, and AWS Systems Manager can be used to streamline operations and optimize workloads.

Step 4: Continuous Monitoring and Improvement

Regularly assess and refine your architecture using monitoring tools such as AWS CloudWatch, AWS Trusted Advisor, and AWS Security Hub.

Conclusion

The AWS Well-Architected Framework is a powerful tool for designing and maintaining cloud applications that are secure, high-performing, resilient, and cost-effective. By following the best practices outlined in this framework, businesses can optimize their cloud infrastructure and achieve long-term success.

Organizations should continuously evaluate and enhance their architectures to align with AWS best practices, ensuring their cloud operations' efficiency, security, and sustainability.

Would you like assistance in implementing these in your AWS environment? Feel free to ask!

Sunday, 19 September 2021

RXJS: Filter => These operators provide techniques for accepting values from an observable source



import { fromof } from 'rxjs';
import { filterfirstlastsingleskipskipWhile
    distinct,taketakeLast  } from 'rxjs/operators';

const source = from([12345]);
const source$ = of(12345);
//filter out non-even numbers
let example = source.pipe(filter(num => num % 2 === 0));
//output: "Even number: 2", "Even number: 4"
//no arguments, emit first value
example = source.pipe(first());
//output: "First value: 1"
example = source.pipe(last());
//output: "Last value: 5"
//emit one item that matches predicate
example = source.pipe(single(val => val === 4));
//output: 4
//skip the first 2 emitted values
example = source.pipe(skip(2));
//output: 3...4...5...
//skip emitted values from source until inner observable emits (6s)
example = source.pipe(skipWhile(a => a < 3));
//output: 3...4...5
of(1234512345).pipe(distinct()).subscribe(console.log);
// OUTPUT: 1,2,3,4,5
//take the first emitted value then complete
example = source$.pipe(take(1));
//output: 1
// take the last 2 emitted values
example = source$.pipe(takeLast(2));
//output: 4,5

Tuesday, 20 April 2021

Time ago pipe : A really simple, lightweight Angular pipe for converting a date string into a time ago

import {
  Pipe,
  PipeTransform,
  NgZone,
  ChangeDetectorRef,
  OnDestroy
from "@angular/core";
import * as moment from 'moment-timezone';
@Pipe({
  name: "timeAgo",
  pure: false
})
export class TimeAgoPipe implements PipeTransformOnDestroy {
  private timernumber;
  constructor(
    private changeDetectorRefChangeDetectorRef,
    private ngZoneNgZone
  ) {}
  transform(valuestringtimeZonestring = '') {
    this.removeTimer();
    let d = new Date(value);
    let now = new Date();
    let sign = Math.sign((now.getTime() - d.getTime()) / 1000);
    let seconds = Math.round(Math.abs((now.getTime() - d.getTime()) / 1000));
    
    if (timeZone) {
      d = moment(value).tz(timeZone);
      now = moment().tz(timeZone);
      sign = Math.sign((now.valueOf() - d.valueOf()) / 1000);
      seconds = Math.round(Math.abs((now.valueOf() - d.valueOf()) / 1000));
    }

    let timeToUpdate = Number.isNaN(seconds) ? 1000 : this.getSecondsUntilUpdate(seconds) * 1000;
    
    this.timer = this.ngZone.runOutsideAngular(() => {
      if (typeof window !== "undefined") {
        return window.setTimeout(() => {
          this.ngZone.run(() => this.changeDetectorRef.markForCheck());
        }, timeToUpdate);
      }
      return null;
    });
    
    let minutes = Math.round(Math.abs(seconds / 60));
    let hours = Math.round(Math.abs(minutes / 60));
    let days = Math.round(Math.abs(hours / 24));
    let months = Math.round(Math.abs(days / 30.416));
    let years = Math.round(Math.abs(days / 365));
    let future = false;
    
    if (sign === -1) {
      future = true;
    }
    
    if (Number.isNaN(seconds)) {
      return "";
    } else if (seconds <= 45) {
      return "just now";
    } else if (seconds <= 90) {
      return (future) ? "Starts in 1 min" : "a minute ago";
    } else if (minutes <= 45) {
      return (future) ? "Starts in " + minutes + " mins" : minutes + " minutes ago";
    } else if (minutes <= 90) {
      return (future) ? "Starts in 1 hour" : "an hour ago";
    } else if (hours <= 22) {
      return (future) ? "Starts in " + hours + " hours" : hours + " hours ago";
    } else if (hours <= 36) {
      return (future) ? "Starts in 1 day" : "a day ago";
    } else if (days <= 25) {
      return (future) ? "Starts in " + days + " days" : days + " days ago";
    } else if (days <= 45) {
      return (future) ? "Starts in 1 month" : "a month ago";
    } else if (days <= 345) {
      return (future) ? "Starts in " + months + " months" : months + " months ago";
    } else if (days <= 545) {
      return (future) ? "a year later" : "a year ago";
    } else {
      // (days > 545)
      return (future) ? years + " years later" :   years + " years ago";
    }
  }
  ngOnDestroy(): void {
    this.removeTimer();
  }
  private removeTimer() {
    if (this.timer) {
      window.clearTimeout(this.timer);
      this.timer = null;
    }
  }
  private getSecondsUntilUpdate(secondsnumber) {
    let min = 60;
    let hr = min * 60;
    let day = hr * 24;
    if (seconds < min) {
      // less than 1 min, update every 2 secs
      return 2;
    } else if (seconds < hr) {
      // less than an hour, update every 30 secs
      return 30;
    } else if (seconds < day) {
      // less then a day, update every 5 mins
      return 300;
    } else {
      // update every hour
      return 3600;
    }
  }
}

Wednesday, 3 June 2020

RXJS Handle multiple API requests in Angular using merge Map and fork Join to avoid nested subscriptions

In this article, I will introduce two techniques to handle multiple requests in Angular by using mergeMap and forkJoin.
Contents:
  1. Problem
  2. subscribe
  3. mergeMap
  4. forkJoin
  5. Combine mergeMap and forkJoin
  6. Performance comparison of subscribe vs mergeMap and forkJoin
Problem
In the real world, we frequently call more than one API in our web applications. When you enter a page, you often make multiple requests to retrieve all the required data, and the results of some API requests are required for subsequent calls.
When we make multiple requests, it’s important to handle them effectively to maintain fast performance for your users while also writing good code.
  1. Call the API to authenticate and retrieve user information
  2. Based on the user information, we call one API to get all posts created by the user.
  3. Based on the user information, we call one API to get all the albums created by the user.
subscribe is a common way to handle requests in Angular, but there are more effective methods. We will first solve our problem using subscribe and then improve on it using mergeMap and forkJoin

Subscribe

Using this technique is quite simple. First, we call one API to get user info, and then we call the other two APIs. We do this in a nested subscription so we can use the results from the first API call.
nested subscription for multiple request
This technique is fine for 2 or 3 requests, but it is hard to read for more requests as your app grows. We would be required to create a lot of nested subscription. That is why we will use RxJS to handle multiple requests.

MergeMap

This operator is best used when you wish to flatten an inner observable but want to manually control the number of inner subscriptions.
So when do we apply mergeMap?
When we need data from the first API request to make requests to the second API.
mergeMap case study
Look at the source code above, we can see that second API needs the user ID from the first API to get data.
Note:
  1. flatMap is an alias for mergeMap.
  2. mergeMap maintains multiple active inner subscriptions at once, so it’s possible to create a memory leak through long-lived inner subscriptions.

ForkJoin

This operator is best used when you have a group of observables and only care about the final emitted value of each. It means that forkJoin allows us to group multiple observables and execute them in parallel, then return only one observable.
When do we apply forkJoin?
We use it when API requests are independent. It means that they do not depend on each other to complete and can execute in parallel.
forkJoin case study

Combine mergeMap and forkJoin

In the real world, there are multiple API requests that depend on the result of another request. So let’s see how can we handle that case by using mergeMap and forkJoin.
Here is a sample to solve our problem:
combine mergeMap and forkJoin
By using these functions, we avoided nested subscriptions and can split code into many small methods.
You have to replace userId inside mergeMap by user that return from map above.