Tuesday, January 27, 2026

Proxies in System Design

In modern system design, proxy servers play a crucial role in improving performance, enhancing security, and ensuring scalability. A proxy acts as an intermediary between a client and the destination server, enabling features like load balancing, caching, anonymity, and more.

Types of Proxies


1. Forward Proxy

 

Position: Between the client and the internet.
Function: Handles requests from clients and forwards them to the appropriate server.
Use Cases:
  • Hiding client IP addresses for privacy
  • Caching frequently accessed content
  • Filtering requests (e.g., blocking restricted websites)

How to use:

  • Configure client to use proxy for outbound traffic.
  • Use squid (recommended) - traditionally used as a forward proxy (and sometimes transparent proxy). It was built for caching web content and controlling outbound traffic.
  • Script:

# squid.conf
http_port 3128
acl allowed_clients src 192.168.1.0/24
http_access allow allowed_clients
# Optional: block certain domains
acl blocked_sites dstdomain .facebook.com .youtube.com

http_access deny blocked_sites

  • Clients configure their browser to use http://proxy-server:3128.
  • NOTE: These are not production scripts, please modify accordingly.

2. Reverse Proxy


Position: Between the internet and backend servers.
Function: Accepts requests on behalf of servers and distributes them efficiently.
Use Cases:
  • Load balancing across multiple servers
  • Caching responses to reduce server load
  • Security features like SSL termination and hiding server IPs
How to use:
  • Place proxy in front of backend servers for inbound traffic.
  • Use Nginx (recommended). It’s lightweight, high‑performance, and widely adopted for load balancing, SSL termination, and protecting backend servers.
  • Script: 
# nginx.conf
server {
    listen 80;
    location / {
        proxy_pass http://backend_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
upstream backend_servers {
    server backend1.example.com;
    server backend2.example.com;
}

  • Requests hit Nginx first, then get distributed to backend servers. 


3. Transparent Proxy


Position: Intercepts requests without altering them.
Function: Operates invisibly to the client, requiring no configuration.
Use Cases:
  • Caching content for faster access
  • Monitoring user activities for compliance or analytics
How to use:
  • Network-level interception, no client config needed.
  • Use Squid + iptables (silent interception)
# Redirect all HTTP traffic to proxy
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 \
  -j REDIRECT --to-port 3128
# squid.conf
http_port 3128 transparent
  • Clients don’t know a proxy is in place; traffic is silently intercepted. \

Benefits of Using Proxies


1. Load Balancing
Distributes incoming requests across multiple backend servers, ensuring no single server is overwhelmed. This improves scalability and reliability.

2. Caching
Stores frequently accessed content closer to the client, reducing backend load and speeding up response times.

3. Security
Adds an extra layer of protection by hiding backend server IPs and preventing direct access.
Example: Filtering out malicious traffic before it reaches the servers.

4. Anonymity
Masks the client’s IP address, providing privacy and anonymity for users browsing the internet.

Conclusion


Proxies are more than just intermediaries - they are powerful tools in system design that enhance performance, security, and user experience. Whether you’re building scalable web applications or securing enterprise systems, understanding proxies is essential for modern architecture.

NOTE: If your application is mostly about web traffic management, scaling, and securing backend APIs, Nginx(reverse-proxy) is the go‑to. If you need client-side anonymity or content filtering, Squid (forward-proxy) is the specialist.

Monday, January 26, 2026

Data Partitioning in System Design

Data Partitioning Techniques: Making Databases Scale Better

As applications grow and data explodes, databases can become bottlenecks. Queries slow down, servers get overloaded, and scaling becomes a nightmare. That’s where data partitioning comes in - a smart way to split your data into manageable chunks so your system stays fast, efficient, and scalable.

The most common partitioning techniques with simple examples and their benefits:

1. Horizontal Partitioning (Sharding)


Description: Splitting data across multiple tables or databases based on rows.
Example 1: You run a global app with millions of users. Instead of storing all user data in one giant table, you split it by region - Asia, Europe, and America. Each shard handles users from its region, reducing load and speeding up queries.

Example 2 (Layman): Think of a library with millions of books. Instead of keeping them all in one giant hall, you split them into different buildings by genre - fiction, science, history. Each building handles its own visitors, so no single hall gets overcrowded.

Sample query/technique (:

```sql

-- Shard by region

CREATE TABLE users_asia (id INT, name TEXT);

CREATE TABLE users_europe (id INT, name TEXT);

-- Application logic decides where to insert:

INSERT INTO users_asia VALUES (1, 'Amit');

INSERT INTO users_europe VALUES (2, 'John'); 

```

Benefits:
  • Distributes traffic across servers
  • Improves performance and scalability
  • Enables regional failover and isolation
Real‑world use case:  

Facebook and Twitter shard user data across multiple servers to handle billions of profiles and posts. Each shard stores a subset of users, often based on user ID ranges, ensuring queries don’t overload a single database. 


Please see Database Sharding topic for a different types and uses of DB sharding.

2. Vertical Partitioning


Description: Splitting data across multiple tables or databases based on columns.
Example 1:  Your user table has profile info (name, email) and activity logs (last login, clicks). Profile data is accessed frequently, while logs are bulky and rarely needed. So you split them into two tables — one for profiles, one for logs.

Example 2 (Layman): Imagine a hospital record. Doctors need quick access to patient details (name, age, allergies), but lab technicians need detailed test results. Splitting the record into two files makes it faster for each group to get what they need.

```sql

-- Profile info

CREATE TABLE user_profile (id INT, name TEXT, email TEXT);

-- Activity logs

CREATE TABLE user_activity (id INT, last_login TIMESTAMP);

-- Join when needed

SELECT p.name, a.last_login

FROM user_profile p

JOIN user_activity a ON p.id = a.id;

```

Benefits:
  • Speeds up queries by isolating hot data
  • Reduces I/O and memory usage
  • Makes schema easier to manage
Real‑world use case:  
LinkedIn separates frequently accessed profile data (name, headline, connections) from less frequently accessed data like activity logs or analytics. 


3. Range Partitioning

Description: Dividing data into partitions based on a range of values.
Example 1: Storing sales data for multiple years. Instead of one massive table, you create partitions for each year - 2021, 2022, 2023. When someone queries 2022 sales, the database skips other years entirely.

Example 2 (Layman): Think of a filing cabinet where folders are arranged by year. If you want 2022 invoices, you go straight to the 2022 folder instead of flipping through everything.

```sql

CREATE TABLE sales (

    id INT, amount DECIMAL, sale_date DATE

)

PARTITION BY RANGE (sale_date) (

    PARTITION p2021 VALUES LESS THAN ('2022-01-01'),

    PARTITION p2022 VALUES LESS THAN ('2023-01-01')

);

``` 

Benefits:

  • Optimizes range-based queries
  • Makes archiving and purging easier
  • Improves indexing and scan speed
Real‑world use case:  
Amazon Redshift and other data warehouses partition sales and transaction data by date ranges (e.g., monthly or yearly).


4. List Partitioning

Description: Partitioning data based on predefined lists of values.
Example 1: Splitting orders by status - pending, shipped, delivered. Order table has statuses: pending, shipped, delivered. Create separate partitions for each status. When you need all "shipped" orders, the database goes straight to that partition.

Example 2 (Layman): Picture a warehouse with separate sections for "pending orders", "shipped orders", and "delivered orders". Workers go directly to the right section instead of searching everywhere.

 ```sql

CREATE TABLE orders (

    id INT, status TEXT

)

PARTITION BY LIST (status) (

    PARTITION p_pending VALUES ('pending'),

    PARTITION p_shipped VALUES ('shipped'),

    PARTITION p_delivered VALUES ('delivered')

);

```

Benefits:
  • Simplifies data access for categorical queries
  • Improves performance for status-based filtering
  • Makes reporting and analytics cleaner
Real‑world use case:  
E‑commerce platforms like Flipkart or Amazon partition orders by status (pending, shipped, delivered) to simplify order management and reporting. 


5. Hash Partitioning

Description: Distributing data across partitions using a hash function.
Example 1:Using a hash of the user ID to evenly spread data across multiple partitions. You hash user IDs to assign each user to one of 10 partitions. This ensures no single partition gets overloaded - even if users are from the same region or have similar profiles.

Example 2 (Layman): Imagine distributing students into classrooms by rolling dice. The dice (hash function) ensures students are spread evenly, so no single room is overcrowded.

```sql
CREATE TABLE users (
    id INT, name TEXT
)
PARTITION BY HASH (id)
PARTITIONS 4; -- evenly spread across 4 partitions
```
Benefits:
  • Balances load automatically
  • Avoids hotspots
  • Great for unpredictable or uniform data
Real‑world use case:  
MongoDB and Cassandra use hash partitioning to distribute documents or rows evenly across nodes. For example, user IDs are hashed to balance load across servers.


6. Composite Partitioning

Description: Combining two or more partitioning methods.
Example 1: Partition sales data by year (range), and within each year, you hash by region. This lets you run fast year-based reports and still balance load across regions.

Example 2 (Layman): Think of a supermarket: first, items are grouped by category (fruits, dairy, snacks). Within fruits, they’re further divided by freshness date. This double-layer organization makes it easy to find what you want.

```sql

CREATE TABLE sales (

    id INT, region TEXT, sale_date DATE

)

PARTITION BY RANGE (sale_date)

SUBPARTITION BY HASH (region)

SUBPARTITIONS 4 (

    PARTITION p2021 VALUES LESS THAN ('2022-01-01'),

    PARTITION p2022 VALUES LESS THAN ('2023-01-01')

);

```

Benefits:
  • Offers flexibility for complex data models
  • Optimizes both performance and scalability
  • Ideal for large enterprise systems
Real‑world use case:  
Oracle Database supports composite partitioning, often used by large banks and telecom companies. For example, telecom call records are partitioned by date (range) and then hashed by customer ID within each date.


Conclusion

Choosing the right partitioning strategy depends on your data model and query patterns.
  • Horizontal and hash partitioning help with scalability.
  • Vertical and list partitioning simplify management.
  • Range and composite partitioning shine in analytical workloads.
By applying these techniques thoughtfully, you can design databases that scale gracefully and deliver faster, more reliable performance.

Thursday, January 22, 2026

Design Patterns

In software engineering, design patterns are tried‑and‑tested solutions to recurring problems in system design. They provide a shared vocabulary for developers and help create flexible, maintainable, and scalable applications. Broadly, design patterns fall into three categories: Creational, Structural, and Behavioral.

In brief, Design Patterns are Reusable Solutions for Common Software Problems.

Creational Patterns

Creational patterns deal with object creation mechanisms, making systems more independent of how their objects are created.

        1. Singleton 

    • Ensures a class has only one instance and provides a global point of access to it.
    • Example: Logger, configuration settings.
                    ```
                    // Ensures only one instance of a class exists.
// Useful for shared resources like loggers or configuration.
class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) instance = new Singleton();
        return instance;
    }
}

                    ``` 

        2. Factory Method  

    • Defines an interface for creating objects but lets subclasses decide which class to instantiate.
    • Example: Document creation in a word processor.
                    ```
                    // Defines an interface for creating objects, letting subclasses decide which type.
// Useful when you want flexible object creation.
interface Document { void open(); } 
class WordDoc implements Document { public void open(){ System.out.println("Word opened"); } }
class PdfDoc implements Document { public void open(){ System.out.println("PDF opened"); } }

class DocumentFactory {
    static Document create(String type) {
        return type.equals("word") ? new WordDoc() : new PdfDoc();
    }
}
                    ```

        3. Abstract Factory  

    • Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
    • Example: GUI toolkit that can create buttons and checkboxes.
                    ```
                    // Provides an interface for creating families of related objects.
// Example: GUI toolkit that can create platform-specific buttons.
interface Button { void render(); }
class WinButton implements Button { public void render(){ System.out.println("Windows Button"); } }
class MacButton implements Button { public void render(){ System.out.println("Mac Button"); } }

interface GUIFactory { Button createButton(); }
class WinFactory implements GUIFactory { public Button createButton(){ return new WinButton(); } }
class MacFactory implements GUIFactory { public Button createButton(){ return new MacButton(); } }
                    ```

        4. Builder  

    • Separates the construction of a complex object from its representation, allowing the same process to create different representations.
    • Example: Building a house with different layouts.
 ```
// Separates construction of a complex object from its representation.
// Useful for creating objects step by step.
class House {
    String walls, roof;
    public String toString(){ return walls + " & " + roof; }
}
class HouseBuilder {
    House house = new House();
    HouseBuilder buildWalls(){ house.walls="Brick Walls"; return this; }
    HouseBuilder buildRoof(){ house.roof="Tile Roof"; return this; }
    House build(){ return house; }
}
```

        5. Prototype  

    • Creates new objects by copying an existing prototype instance.
    • Example: Cloning objects.
```
// Creates new objects by cloning an existing prototype.
// Useful when object creation is expensive.
class Prototype implements Cloneable {
    String field;
    Prototype(String f){ field=f; }
    public Prototype clone(){ try { return (Prototype) super.clone(); } catch(Exception e){ return null; } }
}
```

Structural Patterns

Structural patterns focus on how classes and objects are composed to form larger structures.

        1. Adapter
    • Allows incompatible interfaces to work together by wrapping an existing class with a new interface.
    • Example: Connecting a legacy system to a modern application.
```
// Allows incompatible interfaces to work together.
// Wraps an old system with a new interface.
class OldSystem { void oldRequest(){ System.out.println("Old system"); } }
interface NewSystem { void request(); }
class Adapter implements NewSystem {
    OldSystem old = new OldSystem();
    public void request(){ old.oldRequest(); }
}
```
        2. Decorator  
    • Dynamically adds responsibilities to an object without modifying its structure.
    • Example: Adding scrollbars to a window.
```
// Dynamically adds responsibilities to an object.
// Example: adding scrollbars to a window.
interface Window { void draw(); }
class SimpleWindow implements Window { public void draw(){ System.out.println("Window"); } }
class ScrollDecorator implements Window {
    Window w; ScrollDecorator(Window w){ this.w=w; }
    public void draw(){ w.draw(); System.out.println(" + Scrollbars"); }
}
```
        3. Facade  
    • Provides a simplified interface to a complex subsystem.
    • Example: An API that simplifies the usage of a complex library.
```
// Provides a simplified interface to a complex subsystem.
// Example: hiding multiple steps behind one simple method.
class ComplexLib { void step1(){} void step2(){} }
class Facade {
    ComplexLib lib = new ComplexLib();
    void simple(){ lib.step1(); lib.step2(); System.out.println("Simplified"); }
}
```
        4. Proxy  
    • Provides a surrogate or placeholder for another object to control access to it.
    • Example: Lazy initialization, access control.
```
// Provides a placeholder to control access to another object.
// Useful for lazy loading or access control.
interface Service { void run(); }
class RealService implements Service { public void run(){ System.out.println("Running"); } }
class ProxyService implements Service {
    RealService real = new RealService();
    public void run(){ System.out.println("Proxy check"); real.run(); }
}
```

Behavioral Patterns

Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects.

        1. Observer  
    • Defines a one‑to‑many dependency so that when one object changes state, all dependents are notified automatically.
    • Example: Event handling systems.

      ```
                // Defines a one-to-many dependency so observers are notified of changes.
// Example: event handling systems.
import java.util.*;
interface Observer { void update(String msg); }
class Subject {
    List<Observer> obs = new ArrayList<>();
    void add(Observer o){ obs.add(o); }
    void notifyAllObs(){ for(Observer o:obs) o.update("Event happened"); }
}
```
        2. Strategy  
    • Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
    • Example: Swappable sorting algorithms.
```
// Defines a family of algorithms and makes them interchangeable.
// Example: switching between sorting strategies.
interface Strategy { void execute(); }
class QuickSort implements Strategy { public void execute(){ System.out.println("QuickSort"); } }
class MergeSort implements Strategy { public void execute(){ System.out.println("MergeSort"); } }
class Context {
    Strategy s; Context(Strategy s){ this.s=s; }
    void run(){ s.execute(); }
}
```

        3. Command  
    • Encapsulates a request as an object, allowing for parameterization, queuing, and logging.
    • Example: Undo functionality in applications.
```
// Encapsulates a request as an object.
// Useful for undo/redo functionality.
interface Command { void execute(); }
class PrintCommand implements Command { public void execute(){ System.out.println("Print"); } }
class Invoker { Command c; Invoker(Command c){ this.c=c; } void run(){ c.execute(); } }
```
        4. Iterator  
    • Provides a way to access elements of a collection sequentially without exposing its internal representation.
    • Example: Iterating over a list of objects.
```
// Provides a way to access elements sequentially without exposing representation.
// Example: iterating over a collection.
import java.util.*;
class Demo {
    public static void main(String[] args){
        List<String> list = Arrays.asList("A","B","C");
        for(String s : list) System.out.println(s);
    }
}
```
        5. State  
    • Allows an object to alter its behavior when its internal state changes, appearing to change its class.
    • Example: State machines in game development.
```
// Allows an object to change behavior when its internal state changes.
// Example: state machines in games.
interface State { void handle(); }
class Happy implements State { public void handle(){ System.out.println("Happy"); } }
class Sad implements State { public void handle(){ System.out.println("Sad"); } }
class Context {
    State state;
    void set(State s){ state=s; }
    void request(){ state.handle(); }
}
```

Conclusion

Design patterns are not silver bullets, but they provide proven blueprints for solving common design challenges. By mastering these patterns, developers can write cleaner, more maintainable code and communicate solutions more effectively with their peers.

Tuesday, January 20, 2026

Load Balancers in System Design

When building scalable and reliable systems, one of the most critical components is the load balancer. Its job is simple yet powerful: distribute incoming network traffic across multiple servers so that no single server becomes overwhelmed. This ensures smooth performance, high availability, and a better user experience.

🔑 Key Functions of a Load Balancer

  • Traffic Distribution: Spreads requests evenly across servers.

  • Redundancy & Reliability: Keeps services available even if one server fails.

  • Scalability: Makes it easy to add or remove servers as demand changes.

  • Health Monitoring: Continuously checks server status to avoid routing traffic to unhealthy nodes.

  • Session Persistence: Ensures a user’s session stays on the same server when needed.

  • SSL Termination: Offloads encryption/decryption tasks from backend servers.


🛠️ Types of Load Balancers

  • Hardware Load Balancers 
    Physical devices dedicated to balancing traffic. Often used in enterprise setups.

  •  Software Load Balancers  
    Applications running on standard hardware. Flexible and cost‑effective.

  • Cloud‑Based Load Balancers
    Services offered by cloud providers (AWS ELB, Azure Load Balancer, GCP Load Balancing) that scale automatically and integrate seamlessly with cloud infrastructure.


⚙️ Common Load Balancing Algorithms

  • Round Robin: Sequentially distributes requests across servers.

  • Least Connections: Routes traffic to the server with the fewest active connections.

  • IP Hash: Uses the client’s IP address to determine which server handles the request.

  • Weighted Round Robin: Assigns more requests to servers with higher capacity.


🌐 How Requests Flow Through a Load Balancer

  • Client Request: A user sends a request to the application’s public IP or domain.

  • DNS Resolution: The domain name system translates the domain into the load balancer’s IP.

  • Traffic Reception: The load balancer receives the request.

  • Health Check: It verifies which backend servers are healthy and available.

  • Algorithm Selection: Based on the chosen algorithm (e.g., round robin), it decides where to send the request.

  • Request Forwarding: The request is passed to the selected backend server.

  • Server Processing: The server handles the request and generates a response.

  • Response Return: The server sends the response back to the load balancer.

  • Client Response: Finally, the load balancer forwards the response to the client.

System design diagram illustrating client requests routed through DNS to a load balancer, which forwards traffic to healthy backend servers using algorithms like round robin or least connections, then returns responses to the client.
Diagram showing how a load balancer distributes client requests across backend servers for reliability and scalability.

NOTE: While sending response back, Load Balancer does not use DNS since IP of client is already known.

🚀 Why Load Balancers Matter

Without load balancers, systems risk downtime, bottlenecks, and poor user experience. By intelligently distributing traffic, they provide the foundation for scalable, resilient, and high‑performance applications.

✅ Pros of Using Load Balancers

  • Improved Reliability: If one server fails, traffic is rerouted to healthy servers.

  • Scalability: Easily add or remove servers to handle changing traffic loads.

  • Optimized Performance: Distributes requests to avoid bottlenecks and reduce latency.

  • Security Features: SSL termination and protection against DDoS attacks.

  • Session Persistence: Maintains user sessions across requests when needed.

⚠️ Cons of Using Load Balancers

  • Added Complexity: Requires configuration, monitoring, and maintenance.

  • Cost: Hardware load balancers and cloud services can be expensive.

  • Single Point of Failure: If not properly configured, the load balancer itself can become a bottleneck.

  • Latency Overhead: Adds a small delay due to routing and health checks.


🧠 Conclusion

Load balancers are a cornerstone of modern system design. They ensure that applications remain resilient, scalable, and performant under varying loads. Whether you're deploying a small web app or architecting a global-scale platform, understanding how load balancers work — and choosing the right type and algorithm — is essential for building robust infrastructure.

Database Sharding

Database Sharding: Scaling Your Data the Smart Way

As applications grow, databases often struggle to handle massive amounts of data efficiently. Database sharding is a powerful architecture pattern that solves this problem by splitting large datasets into smaller, more manageable chunks called shards. These shards are distributed across multiple machines or database nodes, improving scalability and performance.

Why Do We Need Sharding?

  • Handles big data workloads
  • Improves query performance
  • Enables horizontal scaling
  • Provides fault isolation

Types of Database Sharding

        1. Key-Based Sharding

               Data is distributed using a hash function.  

               Example: `application_id % 3` → three shards.

        2. Range-Based Sharding

            Data is split by ranges of a column.  

            Example: Names A–P → shard 1, Q–Z → shard 2.

        3. Vertical Sharding

            Data is divided by feature or column groups.  

            Example: On Twitter, user profiles, followers, and tweets are stored in separate shards.

        4. Directory-Based Sharding

            A lookup table maps records to shards.  

            Example: A directory table stores shard IDs for flexible routing.


Advantages

- ✅ Scalability for large datasets  

- ✅ Faster queries due to smaller shard sizes  

- ✅ Fault isolation across shards  


Challenges

- ❌ Complex shard management  

- ❌ Rebalancing data when shards fill up  

- ❌ Cross-shard queries can be slow  


Conclusion

Database sharding isn’t a one-size-fits-all solution, but for applications handling billions of records, it’s often the key to scaling efficiently. By choosing the right sharding strategy, you can build a scalable, distributed database system that grows with your application.

Friday, January 16, 2026

Things to know for System Design

What is System Design?

System design is the process of defining the architecture, components, and
interfaces of a system to meet specific requirements. Here's a brief breakdown:
  1. Identify Requirements: Determine what the system needs to do.
  2. High-Level Design: Outline the system's major components and their interactions.
  3. Detailed Design: Specify the internal workings of each component.
  4. Implementation: Write the code and integrate the components.
  5. Testing: Verify that the system meets all requirements and functions correctly.
  6. Maintenance: Update and improve the system over time.
Functional vs Non-Functional Requirements

Functional: Basic functionalities that the system should offer
Non-Functional: Quality constraints in application like portability, maintainability,
reliability, security etc.

What are the components of System Design?
  1. Architecture: The overall structure of the system, including how components interact and the flow of data.
  2. Components: The individual parts of the system, such as modules, services, or microservices.
  3. Interfaces: The points of interaction between different components or with external systems.
  4. Data Storage: How and where data is stored, including databases, data warehouses, and data lakes.
  5. APIs (Application Programming Interfaces): Interfaces that allow different components or systems to communicate with each other.
  6. Security: Measures to protect the system and data from unauthorized access and vulnerabilities.
  7. Scalability: The system’s ability to handle increased load and expand as needed.
  8. Performance: Ensuring the system operates efficiently and meets performance requirements.
  9. Fault Tolerance and Reliability: The system’s ability to continue functioning correctly even when parts fail.
System Design Life Cycle | SDLC (Design) 

This is actual and practical way for proceeding to solve any design problem:
  1. Requirements
  2. Estimation and Constraints
  3. HLD
  4. LLD
  5. Data Model Design
  6. API Design
  7. Identify and Resolve Bottlenecks
Structured Analysis and Structured Design

Structured Analysis is a technique used to understand and define the
requirements of a system. It involves creating models that represent the system's
functions, data, and control flow. The primary goal is to convert system
requirements into a blueprint for development.
[Data Flow Diagrams (DFDs), Entity-Relationship Diagrams (ERDs), State-Transition
Diagrams, Process Specifications] .

Structured Design follows Structured Analysis and focuses on creating a
blueprint for constructing the system. It uses the results of the analysis phase to
design the system architecture and components.
[Modularity, Top-Down Design, Structure Charts, Coupling and Cohesion]

Please see other posts for system design key concepts.

Proxies in System Design

In modern system design, proxy servers play a crucial role in improving performance, enhancing security, and ensuring scalability. A proxy a...