Programming Language Diaspora

Aatir NadimAatir Nadim
25 min read

“I tell computers to do things, sometimes they listen.”

At its heart, every programming language ever created is empirically about communicating with a machine. You instruct a mechanical device in a manner it understands and it performs deterministic actions. It either works as expected, or it does not. Irrespective, a machine always does what it’s told.

It is possible that what you instructed it to do, does not logically sync with what you expected it to do, hence the concept of errors, exception handling and so forth.

As mentioned in my previous article, you want to get to the crux of something, take it upon yourself to design it. Analyse its requirements, its niche (if at all), its shortcomings, its room of growth and last but not least, how would it pan out if a certain element or feature were absent from it, or in other words, what role does its features play individually and together to comprise the language as it is perceived.

So what makes a programming language tick? How would you go about solving your particular niche if it meant, developing your own programming language..? (humor me..!) And, why, in the name of sanity, are there soooo many programming languages…?

A crowded world; a common vision

At its core, a programming language is a bridge. It is a meticulously crafted lexicon and grammar designed to translate human intent into machine execution. But with hundreds of active languages in existence—from titans of industry to esoteric academic experiments—a fundamental question arises: why create another one? The digital landscape is not wanting for options.

The answer is deceptively simple: vision. Every significant programming language, from the venerable C++ to the modern Rust, was not born into a vacuum. It was conceived to fill a niche, to sand down the rough edges of a predecessor, or to tackle an entirely new class of problems with a novel philosophy.

No language is a silver bullet; each is a specialized tool forged for a purpose. The perceived verbosity of Java gave rise to more concise JVM languages. The memory safety pitfalls of C++ inspired the creation of Rust. The challenge of concurrency in the age of multi-core processors was a primary catalyst for Go.

Modern software systems comprise of multiple services, written in a spectrum of languages, each befitting their corresponding service.

There is no single group of representatives or a central regulating authority which dictates what languages will be written and how. Regular, passionate, well-read, experiment-minded people write these languages, on a regular basis. While estimates vary, thousands of programming languages exist, with multitudes added every day.

Clearly, this is not an elusive feat.

But it takes time. It takes time to consider several key concepts which will be integral to any programming language. The ultimate goal of a language is to be of use in the industry. These are not just features on a checklist; they are a series of critical trade-offs that define a language's character, its strengths, and its inevitable weaknesses. The choices made here dictate what the language will excel at and where it will struggle.

Syntax

The most immediate, human-facing aspect of a language. Is it verbose and explicit like Java, or concise and minimalist like Python? Is its structure C-like with curly braces and semicolons (C++, Java, Go, JavaScript), or does it rely on significant whitespace and indentation (Python)? Syntax directly impacts readability, learning curve, and developer preference.

Programming Paradigm

A language's philosophy on how to structure code. While many modern languages are multi-paradigm, they often champion one. Object-Oriented Programming (OOP), which bundles data with the methods that operate on it, is central to Java, C#, Scala, etc.
Others might favour procedural, functional, or concurrent-first approaches. This choice fundamentally shapes how a developer "thinks" in that language.

Recurring, all these elements are key clues of what concern or niche, its creator wanted to address.

Scope

How does the language manage the visibility and lifetime of variables and functions? Most modern languages use lexical (or static) scope, where visibility is defined by the code's physical structure (e.g., inside a function or a block). This design choice is critical for writing predictable and maintainable code.

Design patterns are implemented with the help of scope. For better readability, modularity, security and real-world entity-relation mapping, efficient scope handling is paramount.

Debugging & Error Handling

How does a language help a developer when things go wrong? This isn't just about external tools. It's about language-level features: Does it have a clear type system that catches errors at compile time (Rust, Go, Java)? Are its runtime error messages and stack traces informative (Python)?
Does it use exceptions (Java, Python) or return-value error handling (Go)?

(Personally I prefer segregating logic via try/catch, but, again, its upto the creator and the end user)

Implementation Venues

What is the language built to create? The answer dictates its core features.

  • Systems Programming (C++, Rust): Requires direct memory access, high performance, and minimal runtime overhead.

  • Web Frontend (JavaScript): Needs to run in a browser, manipulate the DOM, and handle asynchronous events. It is understood by the browser engines.

  • Enterprise Backend (Java, Go): Demands robustness, scalability, strong networking libraries, and concurrency features. Many elements essential to an industry-ready backend are built-in or provided by healthy and verified libraries.

  • Scripting & Data Science (Python): Prioritizes ease of use, rapid development, and a rich ecosystem of third-party libraries.

Memory Management

One of the most significant philosophical divides.

  • Manual Management (C++): The programmer is given full control and full responsibility for allocating and deallocating memory. This offers maximum performance but is a notorious source of bugs (e.g., memory leaks, dangling pointers).

  • Automatic Garbage Collection (Java, Python, Go, JavaScript): A runtime process (the "garbage collector") automatically identifies and frees up memory that is no longer in use. This drastically improves developer safety and productivity at the cost of some performance overhead and potential unpredictability (e.g., "stop-the-world" GC pauses).

  • Ownership Model (Rust): A revolutionary third way. A set of compile-time rules ensures memory safety without a runtime garbage collector, aiming for the best of both worlds: safety and performance.

Execution Model (Compiled vs. Interpreted):

How does the source code become machine code?

  • Ahead-of-Time (AOT) Compilation (C++, Go, Rust): The entire source code is translated into a native binary executable before execution. This results in the highest possible performance.

  • Interpretation & Just-in-Time (JIT) Compilation (Python, JavaScript): Code is read and executed line-by-line by an interpreter. Modern interpreters often include a JIT compiler that identifies "hot" code paths and compiles them to machine code on the fly to improve performance. This model offers greater platform independence and development flexibility. Java sits in a hybrid middle ground, compiling to bytecode which is then interpreted/JIT-compiled by the Java Virtual Machine (JVM).

Concurrency and Parallelism

How does the language handle doing multiple things at once? This is a defining feature of modern language design. The models vary widely, from OS-level threads (C++, Java), to event loops (JavaScript), to language-native lightweight threads and channels (Goroutines in Go), to fearless, data-race-free concurrency primitives (Rust). Does the language leave thread scheduling entirely to the Operating System (a 1:1 model, where one language thread maps to one OS thread), or does its runtime have its own scheduler? Go is famous for its M:N scheduler, which maps M green threads (goroutines) onto N OS threads, allowing it to manage hundreds of thousands of concurrent tasks with high efficiency directly within the language runtime. (Reason for its inception)

Hierarchy

You want to check out a full blown programming language tree, hit this svg url in the browser: p-tree

This is an isolated rendition of a programming language tree, comprising of languages we deem important for the context of this article.

C: The Godfather

C is the “Latin“ of programming languages. It introduced the procedural, curly braces {} temperament, which is used to define blocks and demarcate scopes, almost everywhere, similar to indentation in python.

It gives the programming raw, direct access to memory management. Its memory model of stack and heap are the sum and substance of basic memory manipulation, across languages. It is light, kernel-centric and incredibly effective.

Showcasing the crux of C, i.e. memory management.

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* next;
};

int main() {
    struct Node* head = NULL;
    struct Node* second = NULL;
    struct Node* third = NULL;

    head = (struct Node*)malloc(sizeof(struct Node));
    second = (struct Node*)malloc(sizeof(struct Node));
    third = (struct Node*)malloc(sizeof(struct Node));

    head->data = 1;
    head->next = second;
    second->data = 2;
    second->next = third;
    third->data = 3;
    third->next = NULL;

    printf("Walking through the linked list:\n");
    struct Node* current = head;
    while(current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }

    printf("NULL\n");


    /**
     * This is the important section of the code where we release the memory allocated for the linked list.
     * It is crucial to free each node to prevent memory leaks.
     * The code iterates through the linked list, freeing each node one by one.
     */

    printf("Releasing memory...\n");
    current = head;
    struct Node* nextNode;
    while(current != NULL) {
        nextNode = current->next;
        free(current);
        current = nextNode;
    }
    printf("Memory released successfully.\n");
    return 0;
}

The Linux kernel is written almost entirely in C. The core utilities of Unix/Linux (ls, grep, cat, mv) are C programs. C is the lingua franca for interacting with the OS kernel. When your program wants to open a file, create a process, or send data over the network, it ultimately does so through a C-based Application Binary Interface (ABI) provided by the operating system.

With some help, this is a general implementation of the cat command in linux.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
  // argc is the argument count, argv is the array of argument strings.
  // argv[0] is the program name, argv[1] would be the filename.
  if (argc < 2) {
    fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
    exit(1);
  }

  const char *filename = argv[1];
  FILE *file_pointer;

  // `fopen` is a C standard library function that asks the OS to open a file.
  // "r" means open for reading.
  file_pointer = fopen(filename, "r");

  // Imp: Always check if the OS call succeeded.
  // fopen also returns NULL in case of permissions issues.
  if (file_pointer == NULL) {
    perror("Error opening file");  // perror prints a system error message
    exit(1);
  }

  // Read the file character by character until we reach the End-Of-File (EOF).
  int character;
  while ((character = fgetc(file_pointer)) != EOF) {
    // `putchar`, here prints each character, to the standard output or stdout.
    putchar(character);
  }

  // `fclose` tells the OS we are done with the file, so it can free resources.
  // again, this is very important, there is no concept of automatically freeing up unused
  // memory.
  fclose(file_pointer);

  return 0;
}

C++: The Heir

A direct evolution which started with the principle of “C with classes“. Added object oriented programming or OOP without sacrificing performance (based on zero-cost abstraction).

High-level-abstractions <-> Low-level-controls

This created a language of immense power and staggering complexity, with the same manual memory management pitfalls as C. This complexity and unsafety are the primary drivers for the creation of Java, Go, and Rust.

Consider a multiplayer video game. It has to handle 3D graphics, physics simulations, audio processing, network optimisations, lag and latency tolerance, and, most importantly, the logic of the game, all the while delivering the video at around 60 frames per second (60 fps). C++ is the only language with the low-level control to talk to GPU and high-level OOP implementation (abstraction, inheritance, polymorphism, etc) to help handle millions of lines of code efficiently.

Consider this code snippet for reference.

#include <iostream>
#include <vector>
#include <string>
#include <memory>

using namespace std;

// We define a generic "GameObject" blueprint.
// The `virtual` keyword enables polymorphism, allowing different objects to be treated uniformly.
class GameObject {

  public:
  virtual ~GameObject () {
    cout<<name<<" destroyed "<<endl;
  }

  virtual void update() = 0; // pure virtual function, any subclass MUST implement this.

  void setName(string& new_name) {
    name = new_name;
  }  

  protected:
  string name;

};

// Player subclass
class Player: public GameObject {
  public:
  Player(const string& playerName) {
    name = playerName;
    cout<<"Player "<<name<<" has entered the game"<<endl;
  }
  /*
  All the attributes that a player may have and all the actions a player may make, which may include a change in the current game frame or the upcoming frames, all activity including the assocaition of the player object with the rest of the objects in a game frame may go here
  */

  void update() override {
    // Player specific logic will go here
    cout<<"Updating player "<<name<<endl;
  }


};

// Enemy subclass
class Enemy: public GameObject {
  public:
  Enemy(const string& enemyName, int damage) {
    this->damage = damage;
    name = enemyName;
    cout<<enemyName<<" (enemy) has spawned"<<endl;
  }

  /*
  Same as for the player object, all the data attributes and member functions of the enemy including its interaction with some other object in a game frame, may go here.
  */

  void update() override {
    cout<<"Updating enemy "<<name<<" planning to do "<<damage<<endl;
  }

  private:
  int damage;
};


int main() {

  // A vector to hold all objects in our "scene".
  // We use std::unique_ptr for automatic memory management (RAII).
  // When the unique_ptr goes out of scope, the memory is automatically freed. No manual `delete`!
  vector<unique_ptr<GameObject>> scene;

  scene.push_back(make_unique<Player>("Aatir"));
  scene.push_back(make_unique<Enemy>("Goblin", 10));
  scene.push_back(make_unique<Enemy>("Orc", 30));


  cout << "\n--- Game Loop Begins ---" << endl;

  for(const auto& obj: scene) {
    obj->update();
  }

  cout << "\n--- Game Loop Ends ---" << endl;

  // The 'scene' vector is about to be destroyed.
  // The std::unique_ptr in the vector will automatically call 'delete'
  // on each GameObject, cleaning up all memory.
  // Notice the "destroyed" messages print automatically.


  return 0;
}

Its output →

Player Aatir has entered the game
Goblin (enemy) has spawned
Orc (enemy) has spawned

--- Game Loop Begins ---
Updating player Aatir
Updating enemy Goblin planning to do 10
Updating enemy Orc planning to do 30

--- Game Loop Ends ---
Aatir destroyed 
Goblin destroyed 
Orc destroyed

Another field of implementation would include High Frequency Trading (HFT).

Java: Flagship OOP Language

Primary principle will be OOP, but how to make something safer and portable..?

It borrowed the C-style syntax and the concepts of classes and objects. It completely abandoned manual memory management in favor of a Garbage Collector (GC) and introduced the Java Virtual Machine (JVM) to achieve "write once, run anywhere." It traded raw performance for safety and portability. Java's popularity stems from its versatility, platform independence, robust security features, and strong developer community. It's a reliable choice for various applications, from enterprise systems to mobile apps and IoT platforms. The "write once, run anywhere" philosophy and the Java Virtual Machine (JVM) contribute to its cross-platform compatibility.

The vast majority of large-scale web services for banking, e-commerce, and logistics run on Java. Its stability, mature libraries, and the performance of modern JVMs make it perfect for building the complex, long-running back-end systems that power businesses. Frameworks like Spring Boot have made this process incredibly efficient.

The big data ecosystem was built on the JVM. Major platforms like Apache Hadoop, Apache Spark, Apache Kafka, and Elasticsearch are all written in Java or a JVM language like Scala. The JVM's maturity, JIT (Just-In-Time) compiler optimisations for long-running tasks, and multi-threading capabilities make it ideal for processing massive datasets.


import com.google.common.util.concurrent.RateLimiter;
import java.time.Instant;
import java.util.concurrent.*;


/*
  * A simple data pipeline application that simulates log processing.
  * It uses a BlockingQueue to hold raw logs, processes them in parallel,
  * and forwards the processed logs to a downstream service.
  * The application limits the rate of log production to avoid overwhelming the system.
*/


record LogEvent(String sourceIp, Instant eventTime, String message, Instant processingTime) {

}

class DataForwarder {

    // Simulates an asynchronous call to a database or another web service
    public void sendToDownstreamService(LogEvent event) {
        System.out.printf("[FORWARDER] Sending event from IP %s to analytics DB...%n", event.sourceIp());
        // In a real app, this would use an HTTP client or DB connection pool.
        // We'll just sleep to simulate network latency.
        try {
            Thread.sleep(50); // Simulate 50ms network call
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

class LogProcessor {

    private final BlockingQueue<String> rawLogQueue;
    private final ExecutorService processingPool;
    private final DataForwarder dataForwarder;

    public LogProcessor(BlockingQueue<String> rawLogQueue) {
        this.rawLogQueue = rawLogQueue;
        // Create a thread pool with 4 worker threads to process logs concurrently
        this.processingPool = Executors.newFixedThreadPool(4);
        this.dataForwarder = new DataForwarder();
    }

    public void start() {
        System.out.println("LogProcessor started with 4 worker threads.");
        // One thread to pull from the queue and submit tasks to the pool
        Runnable dispatcher = () -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    String rawLog = rawLogQueue.take(); // Blocks until a log is available
                    // Submit the processing of this log to a worker thread
                    processingPool.submit(() -> processAndForward(rawLog));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        };
        new Thread(dispatcher).start();
    }

    private void processAndForward(String rawLog) {
        System.out.println("[WORKER " + Thread.currentThread().getId() + "] Processing log: " + rawLog);
        // Simulate parsing and transformation
        String[] parts = rawLog.split(",");
        if (parts.length == 3) {
            LogEvent event = new LogEvent(
                    parts[0], // sourceIp
                    Instant.parse(parts[1]), // eventTime
                    parts[2], // message
                    Instant.now() // processingTime (enrichment)
            );
            dataForwarder.sendToDownstreamService(event);
        } else {
            System.err.println("Malformed log entry: " + rawLog);
        }
    }

    public void stop() {
        processingPool.shutdownNow();
    }

}

public class DataPipelineApplication {

    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> logQueue = new ArrayBlockingQueue<>(1000);

        // Start the processing engine
        LogProcessor processor = new LogProcessor(logQueue);
        processor.start();

        // it is always good to limit the rate of log production to avoid overwhelming the system

        // Here we use Guava's RateLimiter to limit log production to 10 logs per second
        // This simulates a controlled rate of incoming logs, which is common in real-world applications
        // to prevent spikes that could overwhelm the processing pipeline.
        // In a production system, you might use a more sophisticated rate limiting strategy
        // based on the actual load and capacity of your processing system.
        final RateLimiter rateLimiter = RateLimiter.create(10.0);

        System.out.println("Starting log producer, limited to 10 logs/sec...");
        for (int i = 0; i < 50; i++) {
            rateLimiter.acquire(); // Blocks until a permit is available
            String log = String.format("192.168.1.%d,%s,User logged in", (i % 20), Instant.now());
            logQueue.put(log); // Puts log in the queue, blocks if full
        }

        System.out.println("Producer finished. Waiting 5s for processing to complete...");
        Thread.sleep(5000);
        processor.stop();
        System.out.println("Application shutdown.");

    }
}

Javascript: Language of the web

Fun fact: Javascript, which powers most of the web, was written in just 10 days!!

The name is a marketing trick to ride on Java's popularity. Its real syntactic influence is C (curly braces, for loops, etc.).

A simple, dynamic scripting language to make web pages interactive. Its core concepts (prototypal inheritance, functions as first-class citizens) were inspired more by languages like Self and Scheme. It was never intended for the massive applications it runs today.

It is a weakly typed language, i.e. its variables are not specifically bound to a data type, which is why debugging a JS application can be such a pain.

The core design of Javascript is built around an asynchronous, non-blocking, single-threaded event loop.

This sounds complex, but the idea is simple: a user interface must never freeze. If a user clicks a button to fetch data from a server, the entire page can't lock up while waiting for the response. JavaScript solves this by starting the task (e.g., the network request) and then immediately moving on, free to handle other user actions like scrolling or typing. When the task is complete, it processes the result.

This non-blocking, event-driven nature turned out to be so efficient for I/O-bound tasks that it was brought to the server with Node.js, creating a new paradigm for building fast, scalable network applications.

const express = require("express");

const app = express();

const PORT = process.env.PORT || 3000;

// Simulate a database of users
const fakeDatabase = {
  1: { name: "Alice", role: "Admin" },
  2: { name: "Bob", role: "User" },
};

app.get("/users/:id", (req, res) => {
  const userId = req.params.id;
  console.log(`Fetching user with ID: ${userId}`);
  const user = fakeDatabase[userId];

  setTimeout(() => {
    if (user) {
      console.log(`User found: ${JSON.stringify(user)}`);
      res.json(user);
    } else {
      res.status(404).json({ error: "User not found" });
    }
  }, 2000);
});


app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

How This Relates to Industry:

  • I/O-Bound vs. CPU-Bound: If you were to hit http://localhost:3000/users/1 and immediately hit http://localhost:3000/users/2, you would see both "Request received..." logs print instantly. Then, after one second, both responses would come back at roughly the same time. The setTimeout simulates an I/O operation. The Node.js server doesn't waste a thread waiting for it. This makes Node.js perfect for I/O-bound microservices.

  • The API Gateway: Companies like Netflix and PayPal use Node.js for their API gateways—the front door to all their internal services. The gateway handles tens of thousands of incoming requests, authenticates them, and efficiently proxies them to backend services (which might be written in Java or Go). (For their core backend, written in Java, Netflix works really closely with the Spring team).

Python: The scripting prodigy

Python doesn't descend from C in the same way C++ does, but it's deeply connected. The primary Python interpreter, CPython, is written in C.

Python's core philosophy is the opposite of C's. It prioritizes developer readability and simplicity above all else, abstracting away almost everything about memory and machine operations. It chose dynamic typing for flexibility over the static typing of C. Most noticeably, it works on indentation. Dealing with indentation is a common hassle. (Check out Bython.)

Python has absolute dominion over Data Science and Analytics*.*

# In your terminal: pip install pandas matplotlib
import pandas as pd
import matplotlib.pyplot as plt

# 1. Load Data
# Pandas can read data from CSV, Excel, SQL databases, and more with one line.
# We'll simulate a raw CSV-like data source.
raw_data = {
    'order_id': [1, 2, 3, 4, 5, 6],
    'product_category': ['Electronics', 'Groceries', 'Groceries', 'Toys', 'Electronics', 'Toys'],
    'price': [899.99, 15.50, 25.00, 39.95, 129.99, None], # Note the missing value
    'quantity': [1, 3, 2, 1, 1, 2]
}
df = pd.DataFrame(raw_data)
print("--- Raw Data ---")
print(df)

# 2. Clean Data
# A common task is handling missing values. Let's remove rows with missing prices.
df.dropna(inplace=True)

# 3. Feature Engineering
# Create a new column 'total_sale' by multiplying price and quantity.
df['total_sale'] = df['price'] * df['quantity']
print("\n--- Cleaned & Enriched Data ---")
print(df)

# 4. Analyze & Aggregate
# Use `groupby` to calculate the total revenue per product category.
# This is a powerful, expressive, and highly optimized operation.
category_revenue = df.groupby('product_category')['total_sale'].sum().sort_values(ascending=False)
print("\n--- Revenue by Category ---")
print(category_revenue)

# 5. Visualize
# Create a bar chart of the results with a few lines of code.
plt.figure(figsize=(8, 5))
category_revenue.plot(kind='bar', color='skyblue')
plt.title('Total Revenue by Product Category')
plt.ylabel('Total Revenue ($)')
plt.xticks(rotation=0)
plt.tight_layout()
plt.show() # This will display a chart window

What you can achieve with just a few lines of Python, in terms of information extraction and information based intelligence processing, is nothing short of visionary. This is why, where Python shines, hardly any other language can take its place.

Python is also sometimes referred to as the “better shell“, because of the extensive support it provides in terms of libraries and community.

#!/usr/bin/env python3
import os
import shutil
import tarfile
from datetime import datetime, timedelta

# --- Configuration ---
LOG_DIR = './app_logs' # Use a local dir for safe testing
BACKUP_DIR = './backups'
YESTERDAY = datetime.now() - timedelta(days=1)
YESTERDAY_STR = YESTERDAY.strftime('%Y-%m-%d')

def main():
    """Main function to orchestrate the backup process."""
    print("--- Starting daily log backup script ---")

    # 1. Setup: Ensure directories exist for the demo
    os.makedirs(LOG_DIR, exist_ok=True)
    os.makedirs(BACKUP_DIR, exist_ok=True)
    # Create some dummy log files for yesterday
    for i in range(3):
        with open(os.path.join(LOG_DIR, f'app.{YESTERDAY_STR}.{i}.log'), 'w') as f:
            f.write("This is a dummy log file.")
    # Create a log file for today that should NOT be backed up
    with open(os.path.join(LOG_DIR, f'app.{datetime.now().strftime("%Y-%m-%d")}.0.log'), 'w') as f:
        f.write("This is today's log.")


    # 2. Find yesterday's log files
    try:
        all_files = os.listdir(LOG_DIR)
        files_to_archive = [f for f in all_files if YESTERDAY_STR in f and f.endswith('.log')]

        if not files_to_archive:
            print("No log files from yesterday found. Exiting.")
            return

        print(f"Found {len(files_to_archive)} log files to archive: {files_to_archive}")

        # 3. Create the compressed archive
        archive_name = f'logs_{YESTERDAY_STR}.tar.gz'
        archive_path = os.path.join(BACKUP_DIR, archive_name)

        # The 'with' statement ensures the file is properly closed even if errors occur
        with tarfile.open(archive_path, "w:gz") as tar:
            for filename in files_to_archive:
                full_path = os.path.join(LOG_DIR, filename)
                tar.add(full_path, arcname=filename) # arcname stores file without the source directory path

        print(f"Successfully created archive: {archive_path}")

        # 4. Delete the original files
        print("Deleting original log files...")
        for filename in files_to_archive:
            os.remove(os.path.join(LOG_DIR, filename))

        print("--- Backup script finished successfully ---")

    except FileNotFoundError:
        print(f"Error: Log directory not found at '{LOG_DIR}'")
    except Exception as e:
        # Generic catch-all for any other unexpected errors
        print(f"An unexpected error occurred: {e}")

if __name__ == '__main__':
    main()

Python OS library is a goldmine, and yes, Tar manipulation is that easy.

Go: Hot on concurrency

Literally, Go's designers at Google were C++ programmers frustrated with its complexity and slow compile times. Let that sink in!

They wanted to return to C's simplicity while updating it for the modern era. The syntax is intentionally small and simple. It purposefully omits many C++ features like classes, inheritance, and templates.

Concurrency should be easy. It introduced goroutines and channels as a simple, built-in solution to a problem that was notoriously difficult in C++ and Java. It uses a GC like Java, but one optimized for low-latency systems.

Go shines in cloud-native network services. A microservice needs to be small, fast, and able to handle thousands of concurrent network requests (e.g., API calls, database connections). Go's goroutines allow a server to handle a huge number of connections with very little memory overhead, and its static binaries make it trivial to deploy inside a lightweight Docker container.

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

func requestHandler(writer http.ResponseWriter, req *http.Request) {
    // Log the request
    jobName := req.URL.Query().Get("job")
    if jobName == "" {
        http.Error(writer, "Missing 'job' query parameter", http.StatusBadRequest)
        return
    }

    log.Printf("Request received for job: %s", jobName)

    // --- The Core of Go's Concurrency ---
    // The `go` keyword starts a new goroutine.
    // The main function does NOT wait for it to complete.
    go func() {
        log.Printf("Worker started for job: %s", jobName)
        // Simulate a long-running task like generating a report or processing video.
        time.Sleep(5 * time.Second)
        log.Printf("Worker finished for job: %s", jobName)
    }()

    // Respond to the user immediately.
    // The background job is still running.
    writer.WriteHeader(http.StatusAccepted)
    fmt.Fprintf(writer, "Accepted job '%s'. It will be processed in the background.", jobName)
}

func main() {
    fmt.Println("Starting HTTP server...")

    http.HandleFunc("/process", requestHandler)

    fmt.Println("Registering request handler for /process")

    err := http.ListenAndServe(":8080", nil)

    if err != nil {
        log.Fatalf("Failed to start server: %v", err)
    } else {
        log.Println("Server started successfully")
        fmt.Println("Server started successfully")
    }
}

Go also works as “better bash".

In this generic example, as a CLI tool, this program takes a list of websites as arguments and checks their status concurrently, making it much faster than a sequential Bash script.

package main

// takes a list of websites and check their status concurrently

import (
    "fmt"
    "log"
    "os"
    "sync"
    "net/http"
)

func main() {

    // os.Args[0] is the program name, so we skip it.
    urls := os.Args[1:]

    if len(urls) == 0 {
        fmt.Println("Please provide a list of URLs to check.")
        log.Fatal("No URLs provided")
    }

    // A WaitGroup waits for a collection of goroutines to finish.
    var wg sync.WaitGroup

    // Increment the WaitGroup counter for each URL.
    for _, url := range urls {
        wg.Add(1)

        // implement the core functionality as a go routine. 
        // (this is where the magic happens!!).
        go func(u string) {
            // Decrement the counter when the goroutine completes.
            defer wg.Done()


            resp, err := http.Get(u)

            if err != nil {
                log.Fatal("Error fetching URL:", u, err)
                fmt.Println("Error fetching URL:", u, err)
                return
            }

            // Close the response body.
            defer resp.Body.Close()

            fmt.Printf("Status for %s: [%s]\n", u, resp.Status)

        }(url) // Pass the url as an argument
    }


    // Wait here until all goroutines have called `wg.Done()`
    wg.Wait()

    fmt.Println("All URLs checked successfully.")

}

Rust: The Safe C++

Rust's designers at Mozilla looked at C++ and asked the ultimate question: "Can we have C++'s bare-metal performance and memory safety, without a garbage collector?"

It targets the same problem domain: systems programming where performance is critical (game engines, browsers, operating systems). It embraces high-level abstractions. The Ownership and Borrowing model. This is Rust's revolution. It's a static analysis system that proves, at compile-time, that your program is memory-safe. You get the safety of Java with the performance of C++.

Rust uses the popular Actix Web framework. In an example, we'll create an endpoint that takes a block of text and concurrently counts the frequency of each word. This is a CPU-bound task where Rust's performance and safe concurrency shine.

// In your Cargo.toml file (Rust's package manager config):
// [dependencies]
// actix-web = "4"
// serde = { version = "1.0", features = ["derive"] }
// dashmap = "5" // A concurrent-safe HashMap

use actix_web::{web, App, HttpServer, Responder, HttpResponse};
use serde::Deserialize;
use dashmap::DashMap; // A HashMap that can be safely shared across threads
use std::sync::Arc;

// Define the structure of the incoming JSON request
#[derive(Deserialize)]
struct WordCountRequest {
    text: String,
}

// The core logic function that processes the text
// It's marked `async` because Actix Web is an async framework
async fn count_words(req: web::Json<WordCountRequest>) -> impl Responder {
    let text = req.into_inner().text;
    // Arc (Atomic Reference Counter) allows us to safely share the DashMap
    // with multiple threads (or in this async context, multiple tasks).
    let word_counts = Arc::new(DashMap::new());

    // --- Concurrent Processing ---
    // We split the text by whitespace and process chunks concurrently.
    // In a real high-performance app, you might use a thread pool like Rayon.
    // Here, we'll use a simple parallel iteration over words.
    let words: Vec<_> = text.split_whitespace().map(str::to_lowercase).collect();

    for word in words {
        // DashMap allows us to increment the count from multiple threads/tasks safely,
        // without needing manual locks. It handles the synchronization internally.
        word_counts.entry(word).or_insert(0).add(1);
    }

    // Convert the DashMap to a regular HashMap for JSON serialization
    let result: std::collections::HashMap<_, _> = word_counts
        .iter()
        .map(|entry| (entry.key().clone(), *entry.value()))
        .collect();

    HttpResponse::Ok().json(result)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("Starting server on http://127.0.0.1:8080");
    HttpServer::new(|| {
        App::new().route("/wordcount", web::post().to(count_words))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Rust also shines in embedded systems applications. It offers the same low-level control as C—you can work with raw memory pointers if you need to—but its safety guarantees are a game-changer for reliability. For an IoT device in the field or a core piece of cloud infrastructure, a memory bug can be catastrophic.

// This enum represents the possible states of our traffic light.
// `derive(Debug)` allows us to print the state for debugging.
#[derive(Debug, PartialEq)]
enum LightState {
    Red,
    Green,
    Yellow,
    FlashingRed, // A fault state
}

// A struct to hold the state of our traffic light controller.
struct TrafficLight {
    state: LightState,
    time_in_state: u32, // Time in seconds
}

impl TrafficLight {
    fn new() -> Self {
        TrafficLight {
            state: LightState::Red,
            time_in_state: 0,
        }
    }

    fn tick(&mut self) {
        self.time_in_state += 1;

        // The Rust compiler will force us to handle EVERY possible state
        // of `LightState`. If we add a new state later and forget to update
        // this `match` block, the code will fail to compile. This eliminates
        // a huge category of bugs.
        match self.state {
            LightState::Red => {
                if self.time_in_state >= 30 {
                    self.transition_to(LightState::Green);
                }
            }
            LightState::Green => {
                if self.time_in_state >= 45 {
                    self.transition_to(LightState::Yellow);
                }
            }
            LightState::Yellow => {
                if self.time_in_state >= 5 {
                    self.transition_to(LightState::Red);
                }
            }
            LightState::FlashingRed => {
                // In fault state, do nothing but flash.
                println!("WARNING: System in fault state!");
            }
        }
    }

    fn transition_to(&mut self, new_state: LightState) {
        println!("Transitioning from {:?} to {:?}", self.state, new_state);
        self.state = new_state;
        self.time_in_state = 0;
    }
}

fn main() {
    let mut light = TrafficLight::new();
    println!("Initial state: {:?}", light.state);

    // Simulate 100 seconds of operation
    for i in 1..=100 {
        print!("[t={:03}] ", i);
        light.tick();
    }
}

Conclusion

“Attitude designs life”

Everything you experience in life, a concept, a relation, an event, it is all your perception of it. Try to view programming concepts and, evidently, multiple versions of polished syntax as, not some biblical testament, to which to you have to adhere, but as tools at your disposal, to build some software or some utility, which serves the end customer or machine as intended, efficiently.
Every such concept as illustrated above, has a significance behind its usage. It all comes down to what you want to serve the end consumer and in what manner. Every bit of architecture decision and its implementation is dependant upon it.

Move to comprehend the reason behind why something exists, what special role it has to play and how would it affect what you want to build, if you were to do away with it or opt for some alternative.

Happy building!!

0
Subscribe to my newsletter

Read articles from Aatir Nadim directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aatir Nadim
Aatir Nadim