Sunday, June 7, 2026

Kubernetes Objects and Their YAML Structure

Kubernetes resources (objects) are represented by YAML manifests. Each object serves a specific purpose and contains a set of core configuration fields. Understanding these objects is essential because almost every Kubernetes deployment is built using combinations of these resources.

Kubernetes Object (kind) What it Does (Description) Core Elements in YAML File
Pod The smallest deployable unit in Kubernetes. It represents a single running process and contains one or more tightly coupled containers.
  • apiVersion
  • kind
  • metadata
  • spec.containers (image, name, ports, env)
  • spec.volumes
Deployment Manages a replicated pool of stateless Pods. Handles rolling updates, rollbacks, and ensures a desired number of Pods remain available.
  • apiVersion
  • kind
  • metadata
  • spec.replicas
  • spec.selector (matchLabels)
  • spec.template
StatefulSet Similar to a Deployment, but designed for applications requiring stable identities, ordered deployment, and persistent storage such as databases.
  • apiVersion
  • kind
  • metadata
  • spec.serviceName
  • spec.replicas
  • spec.template
  • spec.volumeClaimTemplates
DaemonSet Ensures all (or selected) Nodes run exactly one copy of a Pod. Commonly used for monitoring agents, logging agents, and networking components.
  • apiVersion
  • kind
  • metadata
  • spec.selector
  • spec.template
Job Creates one or more Pods and ensures that a specified number of them successfully complete before terminating.
  • apiVersion
  • kind
  • metadata
  • spec.backoffLimit (retry count)
  • spec.template
CronJob Creates Jobs on a recurring schedule using Linux cron syntax.
  • apiVersion
  • kind
  • metadata
  • spec.schedule (e.g. */5 * * * *)
  • spec.jobTemplate
Service Exposes Pods through a stable network endpoint and provides load balancing across multiple Pod replicas.
  • apiVersion
  • kind
  • metadata
  • spec.type (ClusterIP, NodePort, LoadBalancer)
  • spec.selector
  • spec.ports (port, targetPort)
Ingress Manages external HTTP/HTTPS access to Services, typically providing URL routing and SSL/TLS termination.
  • apiVersion: networking.k8s.io/v1
  • kind
  • metadata
  • spec.rules (host, paths)
  • spec.tls
ConfigMap Stores non-sensitive configuration data as key-value pairs that applications can consume as environment variables or mounted files.
  • apiVersion
  • kind
  • metadata
  • data (key-value pairs)
Secret Stores sensitive information such as passwords, API keys, certificates, and authentication tokens.
  • apiVersion
  • kind
  • metadata
  • type (e.g. Opaque)
  • data or stringData
PersistentVolumeClaim (PVC) A request for storage by a user. It abstracts the underlying storage infrastructure and allows Pods to consume persistent storage easily.
  • apiVersion
  • kind
  • metadata
  • spec.accessModes
  • spec.resources.requests.storage

Common Structure Shared by Most Kubernetes Objects

Although Kubernetes objects serve different purposes, most YAML manifests share a common top-level structure.

✓ Key Observation

Most Kubernetes resources contain the following mandatory sections:
  • apiVersion — Defines the API group and version.
  • kind — Defines the Kubernetes object type.
  • metadata — Stores names, labels, annotations, and identifiers.

⚠ Important Note

The object-specific behavior is usually defined inside the spec section, while Kubernetes automatically maintains runtime information inside the status section.

Tip

If you understand the structure of a Pod, Deployment, Service, ConfigMap, Secret, and PVC, you can understand approximately 80% of the Kubernetes YAML files encountered in real-world projects.

Saturday, June 6, 2026

DevOps vs DevSecOps vs MLOps vs MLSecOps Lifecycle Comparison


Phase / Category Traditional SDLC & DevOps DevOps Tools DevSecOps Focus DevSecOps Tools ML Lifecycle & MLOps MLOps Tools MLSecOps Focus MLSecOps Tools
1. Plan & Design Define user features, APIs, and software architecture. Jira, Confluence, Miro, Trello Threat modeling, access control, compliance scoping. IriusRisk, Microsoft Threat Modeling Tool Define business targets, data availability, and model metrics. Jira, Confluence, Lucidchart Data privacy threat modeling and AI risk assessment. Privacy-preserving design frameworks, Microsoft EATM
2. Asset Preparation Write application code, build UI components, and manage repositories. VS Code, IntelliJ IDEA, Git, GitHub, GitLab Prevent credential leaks and enforce secure coding practices. GitGuardian, Talisman, Husky Source datasets, perform cleaning, labeling, and feature engineering. DVC, Feast, Labelbox, Snorkel, Airflow Verify data provenance and detect poisoning or bias. Great Expectations, Cleanlab, TruLens
3. Build & Train Compile source code and package application containers. Jenkins, GitHub Actions, GitLab CI/CD, Bitbucket Pipelines Static Application Security Testing (SAST) and dependency scanning. Snyk, Checkmarx, SonarQube, Veracode Train machine learning models, tune hyperparameters, and track experiments. MLflow, Weights & Biases, Kubeflow, Ray Scan open-source models for malware, vulnerabilities, and backdoors. HiddenLayer Model Scanner, Protect AI Guardian
4. Test & Verify Execute unit tests, integration tests, and UI tests. PyTest, JUnit, Selenium, SonarQube Dynamic Application Security Testing (DAST) and infrastructure scanning. OWASP ZAP, Burp Suite, Aqua Security, Trivy Evaluate model quality, accuracy, fairness, and bias. Evidently AI, TruEra, Fiddler, Deepchecks Perform adversarial robustness testing and prompt fuzzing. Counterfit, Adversarial Robustness Toolbox (ART)
5. Deploy Deploy applications or containers into production. Docker, Kubernetes, Terraform, Ansible Secure networking, secrets management, and API key protection. HashiCorp Vault, CyberArk, AWS Secrets Manager Deploy trained models as APIs or batch inference services. TorchServe, Triton, BentoML, Seldon Core Protect AI endpoints from prompt injection and abuse. Lakera Guard, LLM Guard, Langfuse
6. Monitor Monitor infrastructure health, logs, and application performance. Prometheus, Grafana, Datadog, New Relic Detect security incidents and unauthorized access. Splunk, AWS CloudTrail, Wazuh, ELK Stack Monitor data drift, concept drift, and model degradation. Arize AI, WhyLabs, Neptune.ai, Datadog Detect AI attacks, model extraction, and adversarial behavior. HiddenLayer AISPM, Protect AI Radar

Key Differences by Phase

1. Plan & Design

Discipline Primary Goal
DevOps Design software functionality and architecture.
DevSecOps Integrate security and compliance requirements into the design phase.
MLOps Define business objectives, datasets, and model success metrics.
MLSecOps Identify AI-specific risks such as privacy leakage, bias, and misuse.

2. Asset Preparation

Discipline Main Asset
DevOps Source Code
DevSecOps Secure Source Code
MLOps Training Data and Features
MLSecOps Trusted, Verified, and Poisoning-Free Data

3. Build & Train

Discipline Primary Activity
DevOps Compile and package software.
DevSecOps Perform security scanning and vulnerability assessments.
MLOps Train and optimize machine learning models.
MLSecOps Validate model integrity and detect malicious artifacts.

4. Test & Verify

Discipline Verification Focus
DevOps Functional correctness.
DevSecOps Security vulnerabilities and compliance issues.
MLOps Accuracy, fairness, and model quality.
MLSecOps Adversarial robustness and attack resistance.

5. Deploy

Discipline Deployment Target
DevOps Applications
DevSecOps Secure Applications
MLOps Machine Learning Models
MLSecOps Protected AI Systems

6. Monitor

Discipline Monitoring Focus
DevOps Infrastructure and application health.
DevSecOps Security events and threats.
MLOps Data drift, concept drift, and model degradation.
MLSecOps Prompt injection, model extraction, jailbreaks, and AI attacks.

Simple Relationship

If You Manage... You Typically Use...
Traditional Software DevOps
Traditional Software + Security DevSecOps
Machine Learning Models MLOps
Machine Learning Models + Security MLSecOps

Quick Memory Formula

DevSecOps = DevOps + Security

MLOps = DevOps principles applied to Machine Learning

MLSecOps = MLOps + Security

Difference between LangChain's PromptTemplate and ChatPromptTemplate

In LangChain, both PromptTemplate and ChatPromptTemplate are used to create prompts dynamically, but they are designed for different types of models.

Feature PromptTemplate ChatPromptTemplate
Purpose Creates a single text prompt Creates a structured chat conversation
Output String List of messages
Used With Traditional LLMs Chat models
Message Roles No roles Supports systemhuman ,usersystem , assistant, ai , tool roles
Best For Text completion models Modern chat-based models

1. PromptTemplate

PromptTemplate generates a single formatted string.

Example

from langchain.prompts import PromptTemplate

prompt = PromptTemplate(
    template="Explain {topic} in simple terms.",
    input_variables=["topic"]
)

formatted_prompt = prompt.format(topic="Transformers")

print(formatted_prompt)

Output

Explain Transformers in simple terms.

The result is just a string.

How it is sent to an LLM

llm.invoke(
    "Explain Transformers in simple terms."
)

This style was common with older completion models.

2. ChatPromptTemplate

ChatPromptTemplate creates a conversation consisting of multiple messages.

Example

from langchain.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are an expert teacher."),
    ("human", "Explain {topic} in simple terms.")
])

messages = prompt.format_messages(
    topic="Transformers"
)

print(messages)

Output

[
    SystemMessage(
        content="You are an expert teacher."
    ),
    HumanMessage(
        content="Explain Transformers in simple terms."
    )
]

Notice that the result is not a string.

It is a list of message objects.

How it is sent to a chat model

chat_model.invoke(messages)

Internally, the model receives:

System:
You are an expert teacher.

User:
Explain Transformers in simple terms.

Why ChatPromptTemplate Exists

Modern models such as:

  • OpenAI GPT models
  • Anthropic Claude models
  • Google Gemini models

are chat-oriented.

They understand different message roles:

System: You are a helpful assistant.
User: What is Kubernetes?
Assistant: Kubernetes is...
User: Explain it simply.

PromptTemplate cannot naturally represent this structure.

ChatPromptTemplate can.

Side-by-Side Example

PromptTemplate

prompt = PromptTemplate(
    template="""
You are an expert teacher.

Question:
{question}
"""
)

prompt.format(
    question="What is Kubernetes?"
)

Produces:

You are an expert teacher.

Question:
What is Kubernetes?

ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are an expert teacher."),
    ("human", "{question}")
])

prompt.format_messages(
    question="What is Kubernetes?"
)

Produces:

[
    SystemMessage(
        content="You are an expert teacher."
    ),
    HumanMessage(
        content="What is Kubernetes?"
    )
]

Using Message Placeholders

One major advantage of ChatPromptTemplate is support for conversation history.

Example

from langchain.prompts import ChatPromptTemplate
from langchain.prompts import MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    MessagesPlaceholder("chat_history"),
    ("human", "{question}")
])

Then:

prompt.format_messages(
    chat_history=[
        HumanMessage(content="What is Kubernetes?"),
        AIMessage(content="Kubernetes is a container orchestrator.")
    ],
    question="Who created it?"
)

Result:

System: You are a helpful assistant.
User: What is Kubernetes?
Assistant: Kubernetes is a container orchestrator.
User: Who created it?

This is essential for conversational applications.

When to Use Which?

Scenario Recommended
Simple text generation PromptTemplate
Chatbots ChatPromptTemplate
RAG applications ChatPromptTemplate
Agentic AI ChatPromptTemplate
Conversation history ChatPromptTemplate
Modern GPT/Claude/Gemini models ChatPromptTemplate
Legacy completion models PromptTemplate

Practical Recommendation

For most new LangChain projects in 2026:

ChatPromptTemplate

is the preferred choice because nearly all modern LLMs are chat-based, and it works naturally with:

  • RAG pipelines
  • Agents
  • Tools
  • Memory
  • Multi-turn conversations
  • System instructions

Use PromptTemplate mainly when you specifically need to generate a single text string or are working with APIs that expect plain text rather than chat messages.

Friday, June 5, 2026

Transformer Models – Quick Reference Table

Section Concept Description Key Points
1. Definition Transformer Model A neural network architecture introduced in 2017 that uses attention mechanisms to process entire sequences simultaneously. Foundation of GPT, BERT, Gemini, Claude, Llama, etc.
2. Problem Solved Limitations of RNNs/LSTMs Older models processed words sequentially and struggled with long-range dependencies. Slow training, poor scalability, limited memory of earlier words.
3. Core Innovation Self-Attention Allows every token (word/subword) to directly examine every other token in the sequence. Captures context more effectively than sequential processing.
4. Attention Example Pronoun Resolution In a sentence like "The animal didn't cross the street because it was tired", attention helps identify that "it" refers to "animal". Improves contextual understanding.
5. Input Processing Tokenization Converts text into tokens (words, subwords, or characters). Tokens become numerical representations.
6. Embeddings Word Embeddings Converts tokens into dense vectors containing semantic meaning. Similar concepts have similar vector representations.
7. Positional Encoding Position Information Injects word-order information into token embeddings. Necessary because attention alone does not understand sequence order.
8. Transformer Layer Main Building Block Consists of Self-Attention followed by a Feed-Forward Neural Network. Repeated dozens or hundreds of times.
9. Query (Q) Attention Component Represents what information a token is looking for. Used in attention score calculations.
10. Key (K) Attention Component Represents what information a token contains. Compared against queries.
11. Value (V) Attention Component Represents the actual information passed forward. Weighted by attention scores.
12. Attention Formula Scaled Dot-Product Attention Attention(Q,K,V) = softmax(QKT / √d)V Calculates relationships between tokens.
13. Multi-Head Attention Multiple Attention Mechanisms Several attention heads operate in parallel. Different heads learn grammar, context, relationships, etc.
14. Feed-Forward Network Neural Processing Layer Processes attention outputs through dense neural layers. Adds learning capacity and non-linearity.
15. Encoder Understanding Component Reads and interprets input sequences. Used heavily in BERT-like models.
16. Decoder Generation Component Generates output sequences token-by-token. Used heavily in GPT-like models.
17. Encoder-Only Models Understanding Models Focus primarily on language understanding. BERT, RoBERTa.
18. Decoder-Only Models Generative Models Predict the next token repeatedly. GPT, Llama, Claude, Gemini.
19. Encoder-Decoder Models Transformation Models Use both encoder and decoder. T5, BART, machine translation systems.
20. Training Objective Next Token Prediction Predicts the most likely next token from context. Core learning mechanism for GPT-style models.
21. Inference Process Text Generation Generates one token at a time until completion. Produces responses, code, summaries, etc.
22. Parallel Processing Major Advantage Entire sequences can be processed simultaneously during training. Enables efficient GPU utilization.
23. Long-Context Handling Context Awareness Direct token-to-token connections help retain distant information. Better than RNNs/LSTMs for long documents.
24. Scalability Large Model Training Transformer architecture scales effectively to billions of parameters. Key reason for modern LLM success.
25. Modern Applications AI Systems Used in chatbots, code assistants, translation, summarization, search, and multimodal AI. Backbone of modern generative AI.

Transformer Architecture at a Glance

Component Purpose
TokenizationConvert text into tokens
EmbeddingsConvert tokens into vectors
Positional EncodingPreserve word order
Self-AttentionLearn relationships between tokens
Multi-Head AttentionLearn multiple relationships simultaneously
Feed-Forward NetworkProcess attention outputs
EncoderUnderstand input
DecoderGenerate output
Output LayerPredict next token

Transformer Family Comparison

Model Type Architecture Primary Use Cases Examples
Encoder-Only Encoder Classification, Search, Sentiment Analysis BERT, RoBERTa
Decoder-Only Decoder Chatbots, Text Generation, Code Generation GPT, Llama, Claude, Gemini
Encoder-Decoder Encoder + Decoder Translation, Summarization, Question Answering T5, BART

Transformers vs RNN/LSTM

Feature RNN/LSTM Transformer
Sequential Processing Yes No
Parallel Training No Yes
Long-Term Context Handling Limited Excellent
Training Speed Slow Fast
GPU Utilization Poor Excellent
Scalability Limited Excellent
Foundation of Modern LLMs No Yes

Monday, June 1, 2026

React Custom Hooks

React custom hooks are reusable JavaScript functions that encapsulate stateful logic, allowing you to share functionality across multiple components without duplicating code. They act as clean abstractions that combine React's built-in hooks (like useState and useEffect) into a single, cohesive engine.

Core Rules for Custom Hooks

  • Name must start with "use": The prefix use (e.g., useFetch, useAuth) is mandatory so React's linter can identify it and enforce hook rules.
  • Isolated state: Two components using the exact same custom hook do not share state. Each execution initializes an independent sandbox of state and side-effects.
  • Top-level execution: They must be executed only at the top level of your components or other custom hooks, never inside loops, conditions, or nested functions.

Step-by-Step Practical Example

Consider an application requiring network status verification. Instead of hardcoding window event listeners in every individual component, you can isolate the logic inside a single hook.

1. Define the Custom Hook

Create a file named useOnlineStatus.js to manage the underlying state and side effects:

import { useState, useEffect } from 'react';

export function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    // Clean up event listeners on unmount to prevent memory leaks
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

Use code with caution.

2. Consuming the Hook in Components

You can now pull this isolated logic directly into any functional component:

import { useOnlineStatus } from './useOnlineStatus';

export default function StatusBar() {
  const isOnline = useOnlineStatus();

  return (
    <h1>
      {isOnline ? '✅ Connected' : '❌ Disconnected'}
    </h1>
  );
}

Use code with caution.

Common Use Cases

  • Data Fetching: Consolidating fetch workflows, loading states, and error parsing into a structured useFetch module.
  • Form Handling: Creating a useForm utility to track user inputs, handle reset triggers, and run validations dynamically.
  • Event Listeners: Tracking mouse positions, window resizing, or keyboard strokes cleanly with automated event cleanup.
  • Storage Interactivity: Syncing component states directly with localStorage or sessionStorage.
Learning Resource

Explore the official guide on Reusing Logic with Custom Hooks from the React documentation to dive deeper into custom design patterns.

Angular Pipes

1. Introduction & Core Structure

Angular custom pipes are powerful tools designed to transform data directly within your HTML templates. They allow developers to keep presentation layouts clean by extracting heavy data-formatting logic out of templates and into reusable classes.

To build a custom pipe, you implement the PipeTransform interface and decorate the class with the @Pipe metadata token.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'exponentialStrength',
  standalone: true // Standard configuration for modern Angular applications
})
export class ExponentialStrengthPipe implements PipeTransform {
  transform(value: number, exponent = 1): number {
    return Math.pow(value, isNaN(exponent) ? 1 : exponent);
  }
}

Template Consumption

<!-- Basic usage (Returns 2) -->
<p>{{ 2 | exponentialStrength }}</p>

<!-- Parameterized usage (Returns 8) -->
<p>{{ 2 | exponentialStrength:3 }}</p>

2. Real-World Production Examples

Truncate Text Pipe

Limits a string to a safe maximum size limit and appends a customizable fallback visual suffix.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'truncate',
  standalone: true
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, limit = 20, suffix = '...'): string {
    if (!value) return '';
    return value.length > limit ? value.substring(0, limit) + suffix : value;
  }
}

File Size Converter Pipe

Converts raw numerical bytes into highly readable string formats (KB, MB, GB) with strict data boundaries.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'fileSize',
  standalone: true
})
export class FileSizePipe implements PipeTransform {
  private units = ['Bytes', 'KB', 'MB', 'GB', 'TB'];

  transform(bytes: number, decimalPlaces = 2): string {
    if (bytes === 0) return '0 Bytes';
    if (!bytes || isNaN(bytes)) return '-';

    const index = Math.floor(Math.log(bytes) / Math.log(1024));
    const size = (bytes / Math.pow(1024, index)).toFixed(decimalPlaces);

    return `${size} ${this.units[index]}`;
  }
}

Initials Extractor Pipe

Pulls the first character of structural first and last names to render crisp UI user avatars.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'initials',
  standalone: true
})
export class InitialsPipe implements PipeTransform {
  transform(fullName: string): string {
    if (!fullName) return '';

    const parts = fullName.trim().split(/\s+/);
    const firstInitial = parts[0] || '';
    const lastInitial = parts.length > 1 ? parts[parts.length - 1] : '';

    return (firstInitial[0] + (lastInitial[0] || '')).toUpperCase();
  }
}

3. Injecting Services into Custom Pipes

Pipes often need external context or configurations. Modern applications use the global inject() function to pull background services into the formatting block cleanly.

The Service Dependency

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ConfigService {
  getLanguage(): string { return 'en'; }
}

The Injected Pipe

import { Pipe, PipeTransform, inject } from '@angular/core';
import { ConfigService } from './config.service';

@Pipe({
  name: 'age',
  standalone: true
})
export class AgePipe implements PipeTransform {
  private configService = inject(ConfigService);

  transform(birthDate: Date | string): string {
    if (!birthDate) return '';

    const today = new Date();
    const birth = new Date(birthDate);
    let age = today.getFullYear() - birth.getFullYear();
    const monthDiff = today.getMonth() - birth.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
      age--;
    }

    return this.configService.getLanguage() === 'es'
      ? `${age} años`
      : `${age} years old`;
  }
}

4. Pure vs. Impure Performance Benchmarks

Angular splits pipes into two performance execution modes:

  • Pure Pipes (Default): Executes exclusively when primitive values change (String, Number) or reference pointers flip (Array, Object).
  • Impure Pipes: Executes on every single change detection pass, degrading rendering smoothness.

The Profiling Pipeline

@Pipe({ name: 'pureLog', pure: true, standalone: true })
export class PureLogPipe implements PipeTransform {
  private count = 0;
  transform(val: string): string {
    console.log(`Pure: ${++this.count}`);
    return val;
  }
}

@Pipe({ name: 'impureLog', pure: false, standalone: true })
export class ImpureLogPipe implements PipeTransform {
  private count = 0;
  transform(val: string): string {
    console.log(`Impure: ${++this.count}`);
    return val;
  }
}
The Profiling Results
  • Initial Page Painting: Both pure and impure instances run exactly once.
  • Unrelated Click Actions: Button triggers that do not update the data inputs will cause zero pure logs, while the impure pipe logs recalculate constantly, draining CPU frames.

5. Advanced Layout Patterns

Pipe Chaining

Pipes can string along sequentially. Streams parse strictly from left to right.

<!-- Input -> Capitalized -> Sliced at 16 characters -->
<p>{{ 'This is an example string' | uppercase | truncate:16 }}</p>

Streamed Asynchronous Data

<!-- Fetches from backend API stream, resolves value, handles formatting -->
<p>{{ sizeInBytes$ | async | fileSize }}</p>

TypeScript Programmatic Activation

import { Component, OnInit, inject } from '@angular/core';
import { CurrencyPipe } from '@angular/common';
import { FileSizePipe } from './file-size.pipe';

@Component({
  standalone: true,
  providers: [CurrencyPipe, FileSizePipe],
  template: `<p>{{ report }}</p>`
})
export class ReportComponent implements OnInit {
  private currency = inject(CurrencyPipe);
  private fileSize = inject(FileSizePipe);
  report = '';

  ngOnInit() {
    const price = this.currency.transform(29.99, 'USD');
    const size = this.fileSize.transform(1048576);
    this.report = `Asset cost: ${price} | Download payload: ${size}`;
  }
}

6. Guards & Resolvers Core Integration

Pipes can operate directly inside navigation interceptors to filter data models before route assembly completes.

The Guard

import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { map, take } from 'rxjs';
import { DataService } from './data.service';
import { FileSizePipe } from './file-size.pipe';

export const structuralGuard: CanActivateFn = (route) => {
  const router = inject(Router);
  const fileSizePipe = inject(FileSizePipe);

  return inject(DataService).getFileMetadata(route.paramMap.get('id')!).pipe(
    take(1),
    map(file => {
      const sizeStr = fileSizePipe.transform(file.sizeBytes);
      return (sizeStr.includes('GB') && parseFloat(sizeStr) > 5)
        ? router.createUrlTree(['/limit-exceeded'])
        : true;
    })
  );
};

The Resolver

import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { map } from 'rxjs';
import { UserService } from './user.service';
import { InitialsPipe } from './initials.pipe';

export const profileResolver: ResolveFn<any> = (route) => {
  const initialsPipe = inject(InitialsPipe);

  return inject(UserService).getUserById(route.paramMap.get('id')!).pipe(
    map(user => ({
      ...user,
      avatarInitials: initialsPipe.transform(user.name)
    }))
  );
};

7. Isolated Testing & Mocking (Jasmine)

Because pipes are clean classes, regular implementations do not need heavy initialization overhead. When dependencies are present, leverage TestBed.inject.

import { TestBed } from '@angular/core/testing';
import { AgePipe } from './age.pipe';
import { ConfigService } from './config.service';

describe('AgePipe with Mock Service', () => {
  let pipe: AgePipe;
  let mockService: jasmine.SpyObj<ConfigService>;

  beforeEach(() => {
    const spy = jasmine.createSpyObj('ConfigService', ['getLanguage']);

    TestBed.configureTestingModule({
      providers: [AgePipe, { provide: ConfigService, useValue: spy }]
    });

    pipe = TestBed.inject(AgePipe);
    mockService = TestBed.inject(ConfigService) as jasmine.SpyObj<ConfigService>;

    jasmine.clock().install();
    jasmine.clock().mockDate(new Date('2026-06-01'));
  });

  afterEach(() => jasmine.clock().uninstall());

  it('should parse output text inside English localization targets', () => {
    mockService.getLanguage.and.returnValue('en');
    expect(pipe.transform('1990-06-01')).toBe('36 years old');
  });
});

Truncate Text Pipe Tests

import { TruncatePipe } from './truncate.pipe';

describe('TruncatePipe', () => {
  let pipe: TruncatePipe;

  beforeEach(() => {
    pipe = new TruncatePipe();
  });

  it('should create an instance', () => {
    expect(pipe).toBeTruthy();
  });

  it('should truncate text at default limit (20) and add "..."', () => {
    const text = 'This is a very long string for testing';
    expect(pipe.transform(text)).toBe('This is a very long ...');
  });

  it('should honor a custom limit parameter', () => {
    const text = 'Hello World';
    expect(pipe.transform(text, 5)).toBe('Hello...');
  });

  it('should append a custom suffix parameter', () => {
    const text = 'Hello World';
    expect(pipe.transform(text, 5, '!!!')).toBe('Hello!!!');
  });

  it('should return the original string if it is shorter than the limit', () => {
    const text = 'Short';
    expect(pipe.transform(text, 10)).toBe('Short');
  });

  it('should handle empty or null values gracefully', () => {
    expect(pipe.transform('')).toBe('');
    expect(pipe.transform(null as any)).toBe('');
  });
});

2. File Size Converter Pipe Tests

import { FileSizePipe } from './file-size.pipe';

describe('FileSizePipe', () => {
  let pipe: FileSizePipe;

  beforeEach(() => {
    pipe = new FileSizePipe();
  });

  it('should convert bytes to KB correctly', () => {
    expect(pipe.transform(1024)).toBe('1.00 KB');
  });

  it('should convert bytes to MB correctly', () => {
    expect(pipe.transform(1048576)).toBe('1.00 MB');
  });

  it('should respect custom decimal places parameter', () => {
    expect(pipe.transform(1500000, 3)).toBe('1.431 MB');
  });

  it('should return "0 Bytes" when input is 0', () => {
    expect(pipe.transform(0)).toBe('0 Bytes');
  });

  it('should return "-" for null or invalid numbers', () => {
    expect(pipe.transform(null as any)).toBe('-');
    expect(pipe.transform(NaN)).toBe('-');
  });
});

3. Initials Extractor Pipe Tests

import { InitialsPipe } from './initials.pipe';

describe('InitialsPipe', () => {
  let pipe: InitialsPipe;

  beforeEach(() => {
    pipe = new InitialsPipe();
  });

  it('should extract first and last initials and make them uppercase', () => {
    expect(pipe.transform('john doe')).toBe('JD');
  });

  it('should handle single names by returning one initial', () => {
    expect(pipe.transform('Alex')).toBe('A');
  });

  it('should handle middle names by extracting only the first and last initials', () => {
    expect(pipe.transform('John Fitzgerald Kennedy')).toBe('JK');
  });

  it('should handle extra whitespace around names', () => {
    expect(pipe.transform('   Jane   Smith   ')).toBe('JS');
  });

  it('should return an empty string for empty inputs', () => {
    expect(pipe.transform('')).toBe('');
    expect(pipe.transform(null as any)).toBe('');
  });
});

8. Transitioning to Modern Angular Signals

Angular Signals fundamentally update data strategy mechanics. With Signals, custom HTML pipes are completely obsolete.

Instead of routing values through pipes inside templates, use standard computed() signals. They cache calculations efficiently and update targeted elements instantly without running expensive change detection passes.

The Evolution Comparison

❌ The Legacy Template Approach
<div>{{ profileName | uppercase | truncate:10 }}</div>
The Modern Reactive Signals Approach
import { Component, signal, computed } from '@angular/core';

@Component({
  standalone: true,
  template: `<div>{{ managedProfileName() }}</div>` // Evaluates instantly via direct signal node linkage
})
export class ModernProfileComponent {
  // Writable input data stream
  profileName = signal('Jonathan Abernathy');

  // Unified computed transformation state matrix (Fully memoized out-of-the-box)
  managedProfileName = computed(() => {
    const current = this.profileName().toUpperCase();
    return current.length > 10 ? current.substring(0, 10) + '...' : current;
  });
}

Istio Learning Resources

Istio Basic Sample

Learn Microservices using Kubernetes and Istio

Beginner Evaluation Tasks

These tasks are a great place for beginners to further evaluate Istio’s features using this demo installation:

Production Readiness Resources

Kubernetes Objects and Their YAML Structure

Kubernetes resources (objects) are represented by YAML manifests. Each object serves a specific purpose and contains a set of core configu...