<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mhamad El Itawi</title>
    <description>The latest articles on Forem by Mhamad El Itawi (@mhamadelitawi).</description>
    <link>https://forem.com/mhamadelitawi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F700126%2Fd465da8d-c397-48cc-856c-21791843e74e.jpg</url>
      <title>Forem: Mhamad El Itawi</title>
      <link>https://forem.com/mhamadelitawi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mhamadelitawi"/>
    <language>en</language>
    <item>
      <title>🚩 Red flags series #4: Pull request monsters</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Wed, 10 Dec 2025 18:00:00 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/red-flags-series-4-pull-request-monsters-1o0j</link>
      <guid>https://forem.com/mhamadelitawi/red-flags-series-4-pull-request-monsters-1o0j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;📌 This post is one chapter in my Red Flags series. I’m exploring the mistakes, bad practices, and subtle issues we often overlook in day-to-day development. Stay tuned for upcoming posts!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;When one pull request tries to do the work of an entire sprint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A pull request monster is born when a simple change starts collecting friends. A feature here, a refactor there, a quick rename, a small cleanup and suddenly the PR grows into something huge that nobody feels ready to review. It rarely happens on purpose. Someone wants to finish a task, adds a small fix, improves something that has bothered them for months and does a bit of restructuring “while I am here.” Before long, the PR touches dozens of files and feels massive, unfocused and unpredictable.&lt;/p&gt;

&lt;p&gt;Here is where the real pain begins. A monster PR is exhausting to read. Reviewers cannot get through it in one sitting. Important details hide inside unrelated changes. Comments scatter in every direction. The author struggles to explain everything. The reviewer struggles to understand anything. In that confusion, bugs slip through quietly. Testing becomes harder because the PR is no longer one change. It is several changes tied together with no clean separation. When something breaks after merging, identifying the root cause becomes slow and frustrating. And if the team needs a partial rollback, it turns into a nightmare because all the changes are bundled together. You cannot revert one part without dragging the rest of the mess with it.&lt;/p&gt;

&lt;p&gt;PR monsters can be prevented. The idea is simple and powerful. Split work into smaller, focused pull requests. Keep unrelated edits out. Ship pieces early. Communicate clearly what each PR is meant to do. Let every PR solve one problem and solve it well. Smaller PRs improve review quality, reduce risk and help teams move faster with less stress.&lt;/p&gt;

&lt;p&gt;If your pull request requires snacks, breaks and extra courage to review, it might be time to shrink it. Your teammates and your future self will be grateful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://dev.to/mhamadelitawi"&gt;dev.to&lt;/a&gt; for more practical engineering insights.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cleancode</category>
      <category>coding</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>🚩 Red flags series #3: If-else endless tower</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Mon, 08 Dec 2025 18:00:00 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/red-flags-series-3-if-else-endless-tower-33ch</link>
      <guid>https://forem.com/mhamadelitawi/red-flags-series-3-if-else-endless-tower-33ch</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;📌 This post is one chapter in my Red Flags series. I’m exploring the mistakes, bad practices, and subtle issues we often overlook in day-to-day development. Stay tuned for upcoming posts!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;When your logic starts stacking floors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some code doesn’t just grow, it builds upward. A tiny “if” becomes two… then five… then fourteen, until you’re staring at an if-else tower tall enough to cast a shadow over your whole feature. &lt;/p&gt;

&lt;p&gt;The worst part? These towers rise quietly. One quick fix here, one edge case there, and soon the real intent is buried under layers of branching. Reading it feels like climbing stairs, and testing it feels even worse: every new branch triggers a different path, mocks pile up, and the slightest change can make the whole structure wobble.&lt;/p&gt;

&lt;p&gt;New developers don’t approach this structure with confidence, they approach it with caution, like tourists inspecting a suspicious old building.&lt;/p&gt;

&lt;p&gt;Luckily, towers can come down.&lt;/p&gt;

&lt;p&gt;A great first step is replacing the branching chain with a map or dictionary that acts like a clean switch: each key has its behavior, easy to see and easier to extend. When behaviors differ in meaningful ways, polymorphism is even better. Let each variation live in its own class instead of competing inside one giant conditional.&lt;/p&gt;

&lt;p&gt;And for simple cases, the if/return strategy works wonders. Handle special conditions early, return immediately, and keep the logic flat instead of nesting it like Russian dolls.&lt;/p&gt;

&lt;p&gt;Once the logic is split, the code becomes readable, testable, and far easier to change. Adding new behavior becomes an extension, not a renovation project. Your teammates stop tiptoeing around the file, and your future self stops sighing every time they open it.&lt;/p&gt;

&lt;p&gt;If your conditionals require scrolling, courage, and maybe hydration, it’s probably time to bring the tower down. Your team will thank you.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://dev.to/mhamadelitawi"&gt;dev.to&lt;/a&gt; for more practical engineering insights.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cleancode</category>
      <category>coding</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>🚩 Red flags series #2: God functions</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Sun, 07 Dec 2025 18:00:00 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/red-flags-series-2-god-functions-5d5b</link>
      <guid>https://forem.com/mhamadelitawi/red-flags-series-2-god-functions-5d5b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;📌 This post is one chapter in my Red Flags series. I’m exploring the mistakes, bad practices, and subtle issues we often overlook in day-to-day development. Stay tuned for upcoming posts!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;When one function tries to run the universe&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have worked in any codebase long enough, you have probably met a "god function". It is that one function that seems to know everything, do everything, control everything and occasionally summon thunder if you are not careful.&lt;/p&gt;

&lt;p&gt;A god function usually starts with good intentions. Someone just wants to ship a feature quickly. Then a bit of validation gets added. Then a database call. Then some business logic. Then a couple of notifications. Before you know it, the function has grown into a mythical creature that nobody wants to touch during a code review.&lt;/p&gt;

&lt;p&gt;Here is where the real pain begins. A god function mixes responsibilities that should never live together. Reading it feels like scrolling through the entire application in one file. Testing it becomes a nightmare because you need to mock or spin up half the system just to validate a tiny behavior. A simple unit test suddenly requires databases, external services, side effects and heroic patience. And once tests become difficult, people stop writing them which leads directly to more bugs and more fear around changes.&lt;/p&gt;

&lt;p&gt;Changing one line becomes risky because everything is tightly coupled. New developers stare at it the way tourists stare at ancient monuments: with curiosity, fear and confusion.&lt;/p&gt;

&lt;p&gt;The good news is that god functions are not eternal. The solution is straightforward, even if it takes a bit of discipline. Break them into smaller focused functions. Group related behaviors together. Move business logic away from infrastructure and side effects. Create clear boundaries and let each function do one job well.&lt;/p&gt;

&lt;p&gt;Once the responsibilities are separated, the code becomes readable, testable and much easier to reason about. Small functions reduce cognitive load, make refactoring safer and give your team confidence to make changes without fear.&lt;/p&gt;

&lt;p&gt;If your function requires scroll bars and multiple coffee breaks to understand it, it might be time to break the divine cycle. Your future self and your teammates will thank you.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://dev.to/mhamadelitawi"&gt;dev.to&lt;/a&gt; for more practical engineering insights.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cleancode</category>
      <category>coding</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>🚩 Red flags series #1: Hard-coded credentials and configuration</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Sat, 06 Dec 2025 19:43:04 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/red-flags-series-1-hard-coded-credentials-and-configuration-3p6h</link>
      <guid>https://forem.com/mhamadelitawi/red-flags-series-1-hard-coded-credentials-and-configuration-3p6h</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;📌 This post is one chapter in my Red Flags series. I’m exploring the mistakes, bad practices, and subtle issues we often overlook in day-to-day development. Stay tuned for upcoming posts!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;When your code treats secrets like regular variables.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hard-coding secrets and configuration values means putting sensitive data like API keys, tokens, or database URLs directly into your source code. It feels like a harmless shortcut in the moment, but once these values enter your repository, they become long-term technical debt waiting to resurface at the worst possible time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const DB_USER = "admin";
const DB_PASSWORD = "supersecret123";
const DB_HOST = "prod-db.example.com";

const connectionString = `postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}/app`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The biggest issue is that you eventually forget where they are. That temporary token you dropped into a file during crunch time is now spread across branches, backups, and even your Git history. Once a secret enters version control, it becomes essentially immortal. Hard-coding also breaks environment isolation. Staging and production start behaving like they share the same apartment, and one careless commit can accidentally send staging traffic straight into prod.&lt;/p&gt;

&lt;p&gt;Simple changes also become more painful than they should be. Updating a config value suddenly requires a commit, a PR, a review, and a deployment. And because these values live inside the code, more people need repo access, violating the principle of least privilege and increasing exposure. Compliance frameworks like HIPAA absolutely love that… just kidding, they hate it with passion.&lt;/p&gt;

&lt;p&gt;The fix is straightforward: get secrets out of your code and into proper storage. Use environment variables, external config files, or runtime injection. Adopt a vault like HashiCorp Vault or cloud-native solutions such as AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault. In containerized environments, inject secrets through volumes or orchestrator tools so they never touch the image. This keeps access limited, rotation easy, and onboarding safer new developers don’t need sensitive values just to run the app.&lt;/p&gt;

&lt;p&gt;Hard-coding feels fast, but it always comes back to haunt you. Put your secrets and configuration where they belong, and your systems and your audits will be much happier.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://dev.to/mhamadelitawi"&gt;dev.to&lt;/a&gt; for more practical engineering insights.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cleancode</category>
      <category>coding</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>How ChatGPT Was Made: Behind the Scenes of a Large Language Model</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Sun, 26 Oct 2025 18:49:36 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/how-chatgpt-was-made-behind-the-scenes-of-a-large-language-model-38gd</link>
      <guid>https://forem.com/mhamadelitawi/how-chatgpt-was-made-behind-the-scenes-of-a-large-language-model-38gd</guid>
      <description>&lt;p&gt;Over the past few years, ChatGPT has become one of the most widely used AI tools in the world, powering everything from casual conversations to technical coding help, tutoring, writing, customer service, and more. But behind its friendly interface lies a staggering amount of complexity, engineering, and innovation.&lt;/p&gt;

&lt;p&gt;This article pulls back the curtain on how ChatGPT was built  not just as a product, but as a large language model (LLM) trained on massive datasets, guided by human feedback, and optimized for usefulness and safety. We’ll explore the key stages in its development, from gathering data and training the base model, to aligning it with human values and deploying it at scale.&lt;/p&gt;

&lt;p&gt;Along the way, we’ll also highlight deeper insights from AI researcher Andrej Karpathy, who offers a more technical and nuanced view into how models like ChatGPT actually work under the hood.&lt;/p&gt;

&lt;p&gt;Whether you’re a developer, a curious tech enthusiast, or someone building with AI, this guide will help you understand the full pipeline and the challenges behind the magic of ChatGPT.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧠 What Is a Large Language Model?
&lt;/h1&gt;

&lt;p&gt;A Large Language Model (LLM) is a type of artificial intelligence model trained to understand and generate human language. But unlike traditional rule-based programs, LLMs don’t follow hand-coded instructions they learn language patterns by analyzing massive amounts of text.&lt;/p&gt;

&lt;p&gt;At its core, an LLM is a predictive engine. It doesn’t “know” things in the way humans do instead, it learns to guess the next word (or token) in a sequence based on everything that came before it. It might sound simple, but when scaled up with billions of examples and parameters, this ability leads to incredibly powerful behavior: writing essays, translating languages, answering questions, generating code, and even solving math problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 How It Works
&lt;/h2&gt;

&lt;p&gt;Most LLMs today, including ChatGPT are based on the Transformer architecture, introduced in the paper Attention is All You Need. This architecture enables the model to process entire sequences of text in parallel and “pay attention” to different parts of the input, capturing long-range dependencies and meaning.&lt;/p&gt;

&lt;p&gt;Here’s a simplified flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Text is broken into tokens (sub-word units).&lt;/li&gt;
&lt;li&gt;Each token is embedded into a high-dimensional vector.&lt;/li&gt;
&lt;li&gt;These vectors pass through multiple layers of self-attention and feed-forward networks.&lt;/li&gt;
&lt;li&gt;The final layer produces a probability distribution for the next token.&lt;/li&gt;
&lt;li&gt;This process repeats until the output is complete.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📏 What Makes It Large?
&lt;/h2&gt;

&lt;p&gt;The “large” in LLM refers to several factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model size: billions (or even trillions) of parameters (GPT-3 has 175B, GPT-4 likely more).&lt;/li&gt;
&lt;li&gt;Training data: hundreds of billions of tokens from books, web pages, articles, and code.&lt;/li&gt;
&lt;li&gt;Compute resources: large clusters of GPUs or TPUs are required for training.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧬 Emergent Capabilities
&lt;/h2&gt;

&lt;p&gt;Interestingly, scaling up these models doesn’t just make them “better” it gives them new abilities that weren’t explicitly programmed in. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-turn dialogue&lt;/li&gt;
&lt;li&gt;Complex reasoning&lt;/li&gt;
&lt;li&gt;Programming skills&lt;/li&gt;
&lt;li&gt;Adapting tone and writing style&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These emergent behaviors are a big reason why LLMs like ChatGPT feel so surprisingly capable.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 From the Internet to Tokens: Data Collection and Curation
&lt;/h2&gt;

&lt;p&gt;Before a large language model like ChatGPT can learn anything, it needs data and a lot of it. This is the raw material from which the model learns the structure, vocabulary, logic, and quirks of human language.&lt;/p&gt;

&lt;p&gt;But this isn’t just a copy-paste job from the internet. Collecting and curating the training data is one of the most critical and complex steps in building a reliable and safe LLM.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Where the Data Comes From
&lt;/h3&gt;

&lt;p&gt;The training data for models like GPT-3 and GPT-4 spans a wide range of sources, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web pages (via Common Crawl)&lt;/li&gt;
&lt;li&gt;Wikipedia&lt;/li&gt;
&lt;li&gt;Books (public domain and licensed)&lt;/li&gt;
&lt;li&gt;News articles and blogs&lt;/li&gt;
&lt;li&gt;Forums and Q&amp;amp;A sites&lt;/li&gt;
&lt;li&gt;Code repositories (like GitHub)
This mix gives the model a broad understanding of different writing styles, topics, and domains from casual internet slang to formal academic writing and technical documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧹 Cleaning the Data
&lt;/h2&gt;

&lt;p&gt;Raw web data is messy. It contains spam, duplicated pages, broken formatting, offensive content, and sometimes personal information. That’s why heavy filtering and cleaning steps are required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deduplication: Removing repeated documents and boilerplate (e.g., cookie banners, templates).&lt;/li&gt;
&lt;li&gt;Language filtering: Keeping only high-quality examples in supported languages.&lt;/li&gt;
&lt;li&gt;Toxic content filtering: Removing hate speech, profanity, and harmful ideologies.&lt;/li&gt;
&lt;li&gt;PII removal: Scrubbing names, phone numbers, emails, and any personally identifiable information.
OpenAI and others often use custom heuristics and large-scale classifiers to automate this process at scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚖️ Quality vs Quantity
&lt;/h2&gt;

&lt;p&gt;There’s always a trade-off between data volume and data quality. More data generally leads to better generalization, but if the dataset includes too much low-quality or biased content, the model can learn the wrong things.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too much code from a single language might skew the model’s programming skills.&lt;/li&gt;
&lt;li&gt;Too many English examples can make multilingual support weaker.&lt;/li&gt;
&lt;li&gt;Toxic forums can teach the model harmful patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why modern LLM training pipelines invest heavily in data curation, not just collection. The goal is to give the model the richest and most diverse understanding of language possible, without exposing it to misinformation or harmful patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔐 Closed vs Open Datasets
&lt;/h2&gt;

&lt;p&gt;It’s worth noting: many companies don’t release their exact training datasets especially for models like GPT-4. This is partly due to licensing, partly due to safety concerns, and partly to maintain competitive advantage. However, open-source projects like The Pile, RedPajama, and FineWeb attempt to replicate similar datasets for public research.&lt;/p&gt;

&lt;h1&gt;
  
  
  How the Model Reads: Tokenization
&lt;/h1&gt;

&lt;p&gt;To process human language, a large language model first needs to convert it into a format it can understand and that format is tokens. Tokenization is the first transformation that happens to any input text, and it plays a crucial role in how the model sees and reasons about language.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 What Is a Token?
&lt;/h2&gt;

&lt;p&gt;A token is not always a full word. It can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A word: "apple" → 1 token&lt;/li&gt;
&lt;li&gt;A subword: "unbelievable" → "un", "believ", "able"&lt;/li&gt;
&lt;li&gt;A symbol or piece of punctuation: ".", ",", "#" → 1 token each&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tokenization allows the model to efficiently handle any kind of text, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Misspellings&lt;/li&gt;
&lt;li&gt;Slang or compound words&lt;/li&gt;
&lt;li&gt;Multiple languages&lt;/li&gt;
&lt;li&gt;Programming code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This subword-based system is more flexible than working with whole words and helps the model deal with rare or unfamiliar inputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Byte Pair Encoding (BPE) and Variants
&lt;/h2&gt;

&lt;p&gt;Most modern LLMs use Byte Pair Encoding (BPE) or its variants for tokenization. Here's how it works in simple terms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with individual characters or bytes.&lt;/li&gt;
&lt;li&gt;Repeatedly merge the most frequent adjacent pairs.&lt;/li&gt;
&lt;li&gt;Build up a vocabulary of common subword units.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a fixed-size vocabulary typically around 50,000 to 100,000 tokens that can efficiently encode most languages and formats.&lt;/p&gt;

&lt;p&gt;Other models may use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unigram Language Model tokenization&lt;/li&gt;
&lt;li&gt;SentencePiece&lt;/li&gt;
&lt;li&gt;tiktoken (used in OpenAI’s models, optimized for speed and consistency)
##🧮 Why Tokenization Matters
Tokenization shapes how the model “sees” everything:&lt;/li&gt;
&lt;li&gt;It defines the input length limit (e.g., 8k, 32k, or even 128k tokens).&lt;/li&gt;
&lt;li&gt;It affects memory efficiency and compute cost.&lt;/li&gt;
&lt;li&gt;It impacts the granularity of language understanding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The sentence “ChatGPT is amazing!” might be 4 tokens.&lt;/li&gt;
&lt;li&gt;But a long piece of HTML or code might break into hundreds or thousands of tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Token limits are important in production especially when prompting or working with APIs, since they directly affect what the model can “read” or “remember” at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Fun Fact
&lt;/h2&gt;

&lt;p&gt;Even small changes in punctuation or casing can produce completely different tokenizations. For instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"ChatGPT" might be 1 token.&lt;/li&gt;
&lt;li&gt;"Chat GPT" could be 2 tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's why prompt formatting, especially in few-shot or chain-of-thought examples needs to be done carefully to stay within token budgets and avoid unexpected behavior.&lt;/p&gt;

&lt;h1&gt;
  
  
  ⚙️ Pretraining: Learning to Predict
&lt;/h1&gt;

&lt;p&gt;Once the data is cleaned and tokenized, the real magic begins: pretraining. This is where the model starts learning language by trying to predict the next token in a sequence over and over again, across hundreds of billions of examples.&lt;/p&gt;

&lt;p&gt;It sounds simple: given a prompt like “The cat sat on the”, the model’s job is to predict the next most likely token (in this case, probably “mat”). But scaled up with enough data, compute, and smart architecture, this simple task turns into something powerful it becomes the foundation for reasoning, creativity, conversation, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 The Objective: Next-Token Prediction
&lt;/h2&gt;

&lt;p&gt;The core pretraining objective is often referred to as causal language modeling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: a sequence of tokens&lt;/li&gt;
&lt;li&gt;Task: predict the next token in that sequence&lt;/li&gt;
&lt;li&gt;Loss function: how wrong the model's prediction was, averaged over billions of tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process doesn't require labeled data it's self-supervised. The structure is already embedded in the data itself, making it extremely scalable.&lt;/p&gt;

&lt;p&gt;Over time, the model starts to capture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grammar and syntax&lt;/li&gt;
&lt;li&gt;Factual knowledge&lt;/li&gt;
&lt;li&gt;Common reasoning patterns&lt;/li&gt;
&lt;li&gt;Relationships between words, entities, and concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🏗️ Architecture: The Transformer
&lt;/h2&gt;

&lt;p&gt;Pretraining is powered by the Transformer, a deep neural network architecture built with layers of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-attention mechanisms that let the model “look” at different parts of the sequence&lt;/li&gt;
&lt;li&gt;Feed-forward networks to process and transform the inputs&lt;/li&gt;
&lt;li&gt;Positional encodings so the model understands word order&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model typically has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hundreds of layers&lt;/li&gt;
&lt;li&gt;Billions (or trillions) of parameters&lt;/li&gt;
&lt;li&gt;Huge memory and compute requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each layer refines the model’s internal representation of the input sequence building up from low-level syntax to high-level meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  🖥️ Training at Scale
&lt;/h2&gt;

&lt;p&gt;This phase is computationally expensive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Training models like GPT-3 or GPT-4 requires thousands of GPUs/TPUs running in parallel&lt;/li&gt;
&lt;li&gt;Training can take weeks to months&lt;/li&gt;
&lt;li&gt;Cost estimates run into the millions of dollars&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Training also involves smart engineering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gradient checkpointing to reduce memory usage&lt;/li&gt;
&lt;li&gt;Mixed-precision arithmetic to improve performance&lt;/li&gt;
&lt;li&gt;Model parallelism to spread layers across multiple devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every token, every word, every punctuation mark is used to make the model just a little bit better at predicting what comes next.&lt;/p&gt;

&lt;h2&gt;
  
  
  📏 Context Window: Memory Matters
&lt;/h2&gt;

&lt;p&gt;One of the most important constraints in pretraining is the context window: how many tokens the model can see at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPT-3: ~2,048 tokens&lt;/li&gt;
&lt;li&gt;GPT-4-turbo: up to 128,000 tokens (in some versions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A longer context means the model can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remember more information&lt;/li&gt;
&lt;li&gt;Handle longer prompts or conversations&lt;/li&gt;
&lt;li&gt;Perform deeper reasoning over extended input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Context length is often the bottleneck when using LLMs in real applications, especially for long documents, multi-turn chats, or summarization tasks.&lt;/p&gt;

&lt;h1&gt;
  
  
  🛠️ Fine-Tuning: Teaching the Model to Be Helpful
&lt;/h1&gt;

&lt;p&gt;After pretraining, the model has a general understanding of language it can write, complete sentences, and mimic patterns found in its training data. But it’s still just a raw model. It hasn’t been shaped into a helpful assistant, and it doesn’t know how to follow instructions, stay on topic, or avoid saying the wrong thing.&lt;/p&gt;

&lt;p&gt;This is where fine-tuning comes in. It’s the phase where the model is taught to behave more like a tool, to answer questions, follow instructions, and interact conversationally.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎓 Supervised Fine-Tuning (SFT)
&lt;/h2&gt;

&lt;p&gt;The most common approach is Supervised Fine-Tuning. This involves training the model further using a curated dataset of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompts: e.g., "Explain how gravity works"&lt;/li&gt;
&lt;li&gt;Ideal responses: written by humans or rated as high quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During this phase, the model learns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to format answers clearly&lt;/li&gt;
&lt;li&gt;How to politely refuse inappropriate questions&lt;/li&gt;
&lt;li&gt;How to stick to factual or helpful content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pretrained model: might respond to "How do I tie a tie?" with inconsistent, partially correct answers.&lt;/li&gt;
&lt;li&gt;Fine-tuned model: gives a step-by-step, well-structured explanation, potentially with different methods or options.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fine-tuning teaches form, structure, and tone. it’s like a finishing school for language models.&lt;/p&gt;

&lt;h2&gt;
  
  
  💬 Enter ChatML: Structuring Conversations
&lt;/h2&gt;

&lt;p&gt;To support multi-turn dialogue (like you see in ChatGPT), the input format is structured using ChatML, a convention for marking roles in a conversation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a helpful assistant."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"What's the capital of France?"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The capital of France is Paris."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes it easier for the model to understand context&lt;/li&gt;
&lt;li&gt;Helps the system manage multiple participants (user, assistant, system instructions)&lt;/li&gt;
&lt;li&gt;Enables customization (e.g., setting tone, style, or behavior)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s not part of the architecture . it’s a prompt design pattern, but it makes conversational LLMs like ChatGPT behave much more predictably.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Limitations After Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;Even after fine-tuning, the model may still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be overconfident in its responses&lt;/li&gt;
&lt;li&gt;Hallucinate incorrect facts&lt;/li&gt;
&lt;li&gt;Miss subtle cues in multi-turn dialogue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why fine-tuning is often followed by Reinforcement Learning with Human Feedback (RLHF) to further align the model with human judgment and expectations.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧭 Aligning the Model with Human Values: Reinforcement Learning with Human Feedback (RLHF)
&lt;/h1&gt;

&lt;p&gt;After fine-tuning, the model is much more helpful but it’s still not quite ready for the real world. It might give plausible-sounding but incorrect answers, miss social cues, or produce outputs that feel robotic or awkward. To fix this, we need a way to train it not just to be correct, but to be useful, safe, and aligned with human preferences.&lt;/p&gt;

&lt;p&gt;That’s exactly what Reinforcement Learning with Human Feedback (RLHF) does.&lt;/p&gt;

&lt;h2&gt;
  
  
  👥 Step 1: Collecting Human Preferences
&lt;/h2&gt;

&lt;p&gt;The process begins with a simple idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask the model to generate multiple possible responses to a given prompt.&lt;/li&gt;
&lt;li&gt;Have human labelers rank these responses from best to worst.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, given: "How do I explain quantum computing to a 10-year-old?". The model might produce 4–5 different answers. Humans rank them based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clarity and helpfulness&lt;/li&gt;
&lt;li&gt;Factual accuracy&lt;/li&gt;
&lt;li&gt;Friendliness and tone&lt;/li&gt;
&lt;li&gt;Avoidance of jargon or confusing analogies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These ranked examples become the training data for a reward model.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Step 2: Training the Reward Model
&lt;/h2&gt;

&lt;p&gt;The reward model is trained to predict what humans would prefer. It learns to assign a score to any output based on how well it matches human rankings.&lt;/p&gt;

&lt;p&gt;This turns subjective feedback into something that can be optimized. Now, the model can be trained not just to predict the next token, but to maximize its reward score, generate responses that people actually like.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔁 Step 3: Reinforcement Learning (PPO)
&lt;/h2&gt;

&lt;p&gt;With the reward model in place, the base language model undergoes a final training loop using reinforcement learning, typically with an algorithm called Proximal Policy Optimization (PPO).&lt;/p&gt;

&lt;p&gt;In this phase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model generates outputs.&lt;/li&gt;
&lt;li&gt;The reward model scores them.&lt;/li&gt;
&lt;li&gt;The model updates itself to increase the likelihood of producing higher-scoring answers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This feedback loop aligns the model's behavior with human values and expectations, rather than just surface-level correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚨 Guardrails and Reward Caps
&lt;/h2&gt;

&lt;p&gt;One of the risks with RL is that the model can learn to "game" the reward function producing verbose, generic, or overly cautious answers just to play it safe. To prevent this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Engineers add reward clipping and caps to keep behavior within bounds.&lt;/li&gt;
&lt;li&gt;They train the model to refuse harmful or inappropriate requests.&lt;/li&gt;
&lt;li&gt;They fine-tune for humility  encouraging responses like “I’m not sure” when appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RLHF isn’t perfect, but it adds a critical layer of judgment that helps the model behave more like a helpful assistant and less like an auto-completion engine.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧪 Evaluation and Safety Testing: Stress-Testing the Model Before Deployment
&lt;/h1&gt;

&lt;p&gt;After pretraining, fine-tuning, and RLHF, the model is starting to look like the assistant you know as ChatGPT. But before it’s exposed to millions of users, it needs to go through rigorous evaluation and safety testing.&lt;/p&gt;

&lt;p&gt;Why? Because even a highly trained model can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hallucinate incorrect information&lt;/li&gt;
&lt;li&gt;Respond in biased or harmful ways&lt;/li&gt;
&lt;li&gt;Get confused by ambiguous prompts&lt;/li&gt;
&lt;li&gt;Fail in unexpected edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Evaluation helps identify and reduce those failure modes before they become real-world problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧼 Automated Evaluations
&lt;/h2&gt;

&lt;p&gt;Some tests can be done programmatically at scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Toxicity classifiers: Check whether responses contain offensive or harmful language.&lt;/li&gt;
&lt;li&gt;Bias benchmarks: Evaluate whether the model produces unequal results across gender, race, religion, etc.&lt;/li&gt;
&lt;li&gt;Hallucination detection: Use fact-checking models or rule-based systems to spot invented or misleading claims.&lt;/li&gt;
&lt;li&gt;TruthfulQA &amp;amp; HellaSwag: Benchmark tasks designed to test factual accuracy and reasoning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These automated evaluations help track performance across iterations and flag problematic behaviors.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Red Teaming
&lt;/h2&gt;

&lt;p&gt;In addition to benchmarks, companies like OpenAI employ red teams internal or external experts whose job is to "break" the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompting it to produce dangerous instructions (e.g., building weapons)&lt;/li&gt;
&lt;li&gt;Manipulating it into revealing sensitive content&lt;/li&gt;
&lt;li&gt;Crafting adversarial prompts that confuse or mislead the model&lt;/li&gt;
&lt;li&gt;Testing edge cases (e.g., medical, legal, or financial queries)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Red teaming is a kind of ethical hacking for LLMs, helping anticipate how malicious actors might misuse the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 Calibrating Uncertainty
&lt;/h2&gt;

&lt;p&gt;One of the most important alignment goals is teaching the model when not to answer. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saying “I’m not sure” when the model lacks enough information&lt;/li&gt;
&lt;li&gt;Avoiding speculative or made-up responses&lt;/li&gt;
&lt;li&gt;Refusing to respond to unethical, illegal, or unsafe prompts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This behavior is usually trained during both fine-tuning and RLHF stages and verified during evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔄 Continuous Monitoring
&lt;/h2&gt;

&lt;p&gt;Even after deployment, evaluation doesn’t stop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production logs are monitored (often anonymously and with strict privacy safeguards)&lt;/li&gt;
&lt;li&gt;User feedback helps identify weak points&lt;/li&gt;
&lt;li&gt;New tests and benchmarks are added over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every new deployment is followed by iterations of evaluation → training → re-evaluation a continuous loop to improve quality and safety.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧠 Reasoning and External Tools: Extending the Model’s Capabilities
&lt;/h1&gt;

&lt;p&gt;ChatGPT isn't just about completing text it’s about understanding context, reasoning through problems, and even using external tools to get things right. As powerful as language models are, they still have limits. Reasoning and tool use are two of the main ways we push past those limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔄 Chain-of-Thought Reasoning
&lt;/h2&gt;

&lt;p&gt;One breakthrough in prompting large language models is chain-of-thought reasoning: encouraging the model to "think out loud" by explaining its steps before giving an answer.&lt;/p&gt;

&lt;p&gt;Compare these two responses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without reasoning: “The answer is 42.”&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With reasoning: “First, we multiply 6 by 7 to get 42. Therefore, the answer is 42.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By breaking the problem into steps, the model:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Produces more accurate answers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is easier to debug when it fails&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Demonstrates its internal logic more transparently&lt;/p&gt;

&lt;p&gt;This is especially useful in math, programming, logical puzzles, and multi-step instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Using Tools: Beyond the Model’s Memory
&lt;/h2&gt;

&lt;p&gt;Even a powerful LLM can’t store all of human knowledge and it has no access to real-time data unless you give it some way to retrieve it. That’s where tool use comes in.&lt;/p&gt;

&lt;p&gt;Models can be trained or prompted to call external tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web search APIs (to fetch current information)&lt;/li&gt;
&lt;li&gt;Calculators (for precise math)&lt;/li&gt;
&lt;li&gt;Code interpreters (for executing Python or JavaScript)&lt;/li&gt;
&lt;li&gt;Retrieval-Augmented Generation (RAG) systems (to access custom documents or databases)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In OpenAI’s ecosystem, this shows up as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function calling&lt;/li&gt;
&lt;li&gt;Browse with Bing&lt;/li&gt;
&lt;li&gt;Code Interpreter / Python tool&lt;/li&gt;
&lt;li&gt;File upload + document Q&amp;amp;A&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools are either fine-tuned into the model or integrated via structured prompting. The model decides when and how to use them  similar to a human using a calculator or browser when memory isn’t enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Tool Use Is Part of Alignment
&lt;/h2&gt;

&lt;p&gt;Why train models to use tools? Because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces hallucination&lt;/li&gt;
&lt;li&gt;Increases factual accuracy&lt;/li&gt;
&lt;li&gt;Expands capabilities without retraining the base model&lt;/li&gt;
&lt;li&gt;Keeps the model aligned with its limitations (e.g., “I don’t know, but I can look it up”)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a major step toward agent-like behavior LLMs that not only generate language but act purposefully in the world through APIs, databases, and UIs.&lt;/p&gt;

&lt;h1&gt;
  
  
  ✨ Emergent Abilities and Surprising Behavior
&lt;/h1&gt;

&lt;p&gt;One of the most fascinating and still mysterious aspects of large language models is the appearance of emergent abilities. These are capabilities that weren’t explicitly programmed or taught, but seem to arise naturally when models reach a certain scale.&lt;/p&gt;

&lt;p&gt;In other words: the bigger the model, the more surprising things it can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌱 What Are Emergent Abilities?
&lt;/h2&gt;

&lt;p&gt;Emergent abilities are behaviors that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t appear in smaller models&lt;/li&gt;
&lt;li&gt;Suddenly do appear at a certain size or level of training&lt;/li&gt;
&lt;li&gt;Often exceed expectations of what the architecture should be capable of&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-step reasoning: solving logic puzzles, math word problems&lt;/li&gt;
&lt;li&gt;Translation: understanding and generating text across multiple languages&lt;/li&gt;
&lt;li&gt;Zero-shot generalization: completing tasks it was never trained on explicitly&lt;/li&gt;
&lt;li&gt;Code generation: writing functional code in multiple languages&lt;/li&gt;
&lt;li&gt;Style imitation: writing like Shakespeare, Tolkien, or a YouTube influencer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These skills emerge not because someone told the model how to do them, but because the model was exposed to enough examples during training to infer the patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧪 The Role of Scale
&lt;/h2&gt;

&lt;p&gt;Emergent behavior tends to show up when models hit key thresholds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model size (parameters)&lt;/li&gt;
&lt;li&gt;Training data volume (tokens)&lt;/li&gt;
&lt;li&gt;Training compute (epochs × hardware)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This has led to a guiding principle in AI research:&lt;br&gt;
“More data, bigger models, better results but sometimes with surprising leaps in capability.”&lt;/p&gt;

&lt;p&gt;A smaller model might understand English syntax, but only the larger one can write legal contracts or solve geometry problems even if both were trained on similar data.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Why This Matters
&lt;/h2&gt;

&lt;p&gt;Emergence changes how we think about model design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It means capabilities aren’t linear doubling model size doesn’t just make it better, it can make it different.&lt;/li&gt;
&lt;li&gt;It forces caution a more capable model might also be more likely to generate complex, unexpected, or risky outputs.&lt;/li&gt;
&lt;li&gt;It fuels innovation researchers keep pushing boundaries to discover what else these models might learn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve now reached the point where large language models can reason, write, plan, and problem-solve in ways that often surprise even their creators.&lt;/p&gt;

&lt;h1&gt;
  
  
  🚀 Deploying ChatGPT in the Real World
&lt;/h1&gt;

&lt;p&gt;After training, fine-tuning, alignment, and safety testing, the model is finally ready to meet the real world. But deploying something like ChatGPT isn’t as simple as dropping a model into a server. It requires robust infrastructure, thoughtful design, and constant monitoring to ensure it’s safe, scalable, and responsive to millions of users.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧑‍💻 How People Use ChatGPT
&lt;/h2&gt;

&lt;p&gt;ChatGPT is accessible through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The chat.openai.com web interface&lt;/li&gt;
&lt;li&gt;OpenAI’s API (used by developers in apps, bots, plugins, etc.)&lt;/li&gt;
&lt;li&gt;Microsoft products (e.g., Copilot in Word, Excel, GitHub)&lt;/li&gt;
&lt;li&gt;Mobile apps on iOS and Android&lt;/li&gt;
&lt;li&gt;Custom integrations and enterprise solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each interface wraps the same core model but with added layers of formatting, safety, and interaction design.&lt;/p&gt;

&lt;h2&gt;
  
  
  ☁️ Infrastructure at Scale
&lt;/h2&gt;

&lt;p&gt;Under the hood, large models like GPT-4 require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Massive GPU/TPU clusters for inference (not just training)&lt;/li&gt;
&lt;li&gt;Autoscaling infrastructure to handle spikes in demand&lt;/li&gt;
&lt;li&gt;Low-latency API orchestration to keep response times acceptable&lt;/li&gt;
&lt;li&gt;Distributed caching, prompt streaming, and optimization tricks to minimize cost and delay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deploying the model isn’t just about the AI, it’s a full-stack engineering challenge involving systems, networking, DevOps, and platform reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ Safety Layers in Production
&lt;/h2&gt;

&lt;p&gt;Even with all the training and alignment, safety can’t be fully guaranteed especially when new prompts and use cases appear daily. That’s why deployment includes multiple runtime safety systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt moderation: filters or rejects dangerous, unethical, or harmful input&lt;/li&gt;
&lt;li&gt;Output filtering: catches and blocks inappropriate or unsafe responses before they’re shown to the user&lt;/li&gt;
&lt;li&gt;Rate limiting &amp;amp; abuse detection: protects against spam, denial-of-service attempts, and automated misuse&lt;/li&gt;
&lt;li&gt;Policy constraints: restrict usage in sensitive areas (e.g., medical, legal, or political domains)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These layers ensure the model behaves more responsibly, even if the input tries to trick it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔁 Real-Time Feedback and Iteration
&lt;/h2&gt;

&lt;p&gt;The work doesn’t stop after deployment. OpenAI (and similar orgs) continually improve the model through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User feedback (thumbs up/down, comments, flags)&lt;/li&gt;
&lt;li&gt;Anonymized usage logs to detect edge cases or regressions&lt;/li&gt;
&lt;li&gt;Ongoing updates to prompts, training methods, moderation, and tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is part of a continuous deployment cycle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Train → Align → Test → Deploy → Monitor → Improve → Repeat&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  ⚠️ The Challenges of Building Large Language Models
&lt;/h1&gt;

&lt;p&gt;As impressive as large language models are, building them isn’t without major challenges, some technical, some ethical, and many still unsolved. These systems aren’t magic. They’re incredibly complex, expensive, and risky when handled carelessly.&lt;/p&gt;

&lt;p&gt;Let’s look at the core challenges behind building, deploying, and maintaining models like ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  💣 Hallucination
&lt;/h2&gt;

&lt;p&gt;LLMs don’t “know” things, they generate plausible-sounding text based on patterns in data. That means they sometimes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make things up confidently&lt;/li&gt;
&lt;li&gt;Fabricate sources or citations&lt;/li&gt;
&lt;li&gt;Mislead users without realizing it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This issue is known as hallucination, and it’s one of the biggest obstacles to using LLMs in high-stakes domains like medicine, law, or science.&lt;/p&gt;

&lt;p&gt;Efforts to mitigate this include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encouraging humility: responses like “I don’t know” or “I’m not sure”&lt;/li&gt;
&lt;li&gt;Tool integration: using search, code, or databases to ground responses&lt;/li&gt;
&lt;li&gt;Better prompting and alignment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎭 Bias and Fairness
&lt;/h2&gt;

&lt;p&gt;LLMs learn from internet-scale data and that data reflects all the biases of society:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gender and racial stereotypes&lt;/li&gt;
&lt;li&gt;Cultural assumptions&lt;/li&gt;
&lt;li&gt;Political or religious slants&lt;/li&gt;
&lt;li&gt;Offensive or exclusionary language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even after heavy filtering, some of these patterns persist in model behavior.&lt;/p&gt;

&lt;p&gt;To address this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Researchers run bias benchmarks and test prompts&lt;/li&gt;
&lt;li&gt;Models are aligned to avoid repeating harmful stereotypes&lt;/li&gt;
&lt;li&gt;Teams aim to balance representation without over-correction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, bias mitigation remains an open area of research both technically and philosophically.&lt;/p&gt;

&lt;h2&gt;
  
  
  💰 Compute and Cost
&lt;/h2&gt;

&lt;p&gt;Training models like GPT-4 takes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Weeks or months on massive distributed GPU clusters&lt;/li&gt;
&lt;li&gt;Tens of thousands of A100 or H100 chips&lt;/li&gt;
&lt;li&gt;Millions of dollars in cloud costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that’s just training. Running the model for millions of users daily inference at scale, is also expensive.&lt;/p&gt;

&lt;p&gt;This leads to ongoing debates about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessibility (why many models aren’t open-source)&lt;/li&gt;
&lt;li&gt;Sustainability (energy use and environmental impact)&lt;/li&gt;
&lt;li&gt;Monopolization (only a few companies can afford to train models of this size)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 Interpretability
&lt;/h2&gt;

&lt;p&gt;LLMs are often described as “black boxes” they produce high-quality outputs, but we don’t always know why.&lt;/p&gt;

&lt;p&gt;This raises difficult questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What caused the model to choose one answer over another?&lt;/li&gt;
&lt;li&gt;How can we debug bad outputs?&lt;/li&gt;
&lt;li&gt;Can we trust a system we don’t fully understand?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Researchers are exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attention visualization&lt;/li&gt;
&lt;li&gt;Neuron analysis&lt;/li&gt;
&lt;li&gt;Feature tracing
But a clear path to full interpretability is still far away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔐 Privacy and Data Leaks&lt;br&gt;
Because training data comes from public web sources, there’s a risk that models might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memorize personal or sensitive data&lt;/li&gt;
&lt;li&gt;Reveal email addresses, phone numbers, or passwords&lt;/li&gt;
&lt;li&gt;Reproduce private information unintentionally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To combat this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sensitive data is filtered or masked during training&lt;/li&gt;
&lt;li&gt;Models are fine-tuned to avoid revealing such content&lt;/li&gt;
&lt;li&gt;Post-training audits look for memorized patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, the risk is not zero especially in larger models trained on unfiltered or user-contributed datasets.&lt;/p&gt;

&lt;h1&gt;
  
  
  🔮 The Future of Language Models
&lt;/h1&gt;

&lt;p&gt;We’re still in the early days of what large language models can do. ChatGPT, as powerful as it is, is just one step in a fast-moving landscape. Researchers, developers, and companies are rapidly exploring the next frontier of capabilities, architectures, and applications.&lt;/p&gt;

&lt;p&gt;Here’s where things are headed.&lt;/p&gt;

&lt;h2&gt;
  
  
  🖼️ Multimodal Models
&lt;/h2&gt;

&lt;p&gt;Future models won’t just understand text, they’ll process images, audio, video, and more, all in one unified interface. This is already underway:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI’s GPT-4 can handle image inputs (e.g., charts, screenshots, handwritten notes).&lt;/li&gt;
&lt;li&gt;Google’s Gemini and Meta’s LLaVA models push toward full multimodality.&lt;/li&gt;
&lt;li&gt;Audio and speech-based models (like Whisper and Voice Chat) are being layered into conversational systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This unlocks use cases like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual Q&amp;amp;A (e.g., “What’s wrong with this graph?”)&lt;/li&gt;
&lt;li&gt;Document understanding from scanned PDFs&lt;/li&gt;
&lt;li&gt;Seamless voice-to-text-to-action assistants&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧠 Personalization and Memory
&lt;/h2&gt;

&lt;p&gt;Right now, most LLMs are stateless, they don’t “remember” past interactions unless context is explicitly included in the prompt. But that’s changing.&lt;/p&gt;

&lt;p&gt;New models will support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-specific memory: remembering preferences, goals, and past conversations&lt;/li&gt;
&lt;li&gt;Contextual learning: adapting behavior based on previous interactions&lt;/li&gt;
&lt;li&gt;Agent-like workflows: acting over time on your behalf, not just replying to one-off prompts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenAI is already rolling out memory features to select users, and more personalized assistants are on the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 Autonomous Agents and Tool Use
&lt;/h2&gt;

&lt;p&gt;LLMs are becoming more than passive responders, they’re turning into agents that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make decisions&lt;/li&gt;
&lt;li&gt;Use external tools and APIs&lt;/li&gt;
&lt;li&gt;Perform multi-step reasoning or planning&lt;/li&gt;
&lt;li&gt;Navigate web pages, apps, or even operating systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This powers tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AutoGPT, LangChain, and other agent frameworks&lt;/li&gt;
&lt;li&gt;AI copilots that write code, manage tasks, or control environments&lt;/li&gt;
&lt;li&gt;Voice-based assistants that act in real-time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These systems blur the line between model and app, they’re intelligent systems built around LLMs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Open Source vs Proprietary Models
&lt;/h2&gt;

&lt;p&gt;The ecosystem is dividing into two parallel tracks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Closed-source giants like GPT-4, Claude, Gemini, highly capable, tightly controlled&lt;/li&gt;
&lt;li&gt;Open-source challengers like LLaMA, Mistral, Falcon, Mixtral , lightweight, community-driven, rapidly improving&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open-source models are becoming more powerful and more accessible  and are likely to dominate edge, private, and embedded AI use cases in the near future.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ Ethics, Regulation, and AI Governance
&lt;/h2&gt;

&lt;p&gt;As LLMs become more influential, so does the need to ensure they’re:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aligned with human values&lt;/li&gt;
&lt;li&gt;Safe for all users&lt;/li&gt;
&lt;li&gt;Respectful of privacy, consent, and legal frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Governments and organizations are actively working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI safety standards&lt;/li&gt;
&lt;li&gt;Model transparency requirements&lt;/li&gt;
&lt;li&gt;Audits, red-teaming, and public accountability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The future of LLMs isn’t just a technical challenge it’s a social and ethical one, too.&lt;/p&gt;

&lt;h1&gt;
  
  
  ✅ Conclusion: From Prediction to Purpose
&lt;/h1&gt;

&lt;p&gt;The story of how ChatGPT was made is more than just a technical achievement, it’s a glimpse into the future of how we build intelligent systems.&lt;/p&gt;

&lt;p&gt;Instead of crafting detailed logic by hand, we now train models on human language itself, allowing them to learn from billions of examples how we speak, reason, and interact. Every stage, from collecting noisy internet data to aligning responses through human feedback  reflects a new kind of engineering: one that’s less about rules, and more about shaping behavior through data, scale, and iteration.&lt;/p&gt;

&lt;p&gt;As these systems grow more capable, so do the challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we ensure truthfulness and safety?&lt;/li&gt;
&lt;li&gt;How do we reduce bias while preserving diversity of thought?&lt;/li&gt;
&lt;li&gt;How do we make models not just powerful but trustworthy and transparent?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yet, the opportunities are equally massive. We're building tools that can teach, assist, write, reason, and even collaborate. Tools that don't just answer, they understand. That don't just compute, they communicate.&lt;/p&gt;

&lt;p&gt;LLMs like ChatGPT are still evolving. But what’s clear is that we’re witnessing the birth of a new paradigm in computing, one where language itself becomes the interface to intelligence.&lt;/p&gt;

&lt;p&gt;The next chapter? That’s up to all of us to write.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>llm</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>How to Scale Your AWS Architecture: From EC2 to Multi-Region Deployment</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Thu, 31 Jul 2025 07:52:25 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-scale-your-aws-architecture-from-ec2-to-multi-region-deployment-5ag7</link>
      <guid>https://forem.com/aws-builders/how-to-scale-your-aws-architecture-from-ec2-to-multi-region-deployment-5ag7</guid>
      <description>&lt;p&gt;As systems evolve, so does their architecture. What begins as a single EC2 instance can mature into a globally resilient infrastructure. This post walks through the natural progression of architectural decisions teams make as their application, traffic, and reliability needs grow.&lt;/p&gt;

&lt;p&gt;Here’s a high-level journey from the first EC2 instance to a multi-region setup, all through the AWS lens.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. The Humble Beginning: One EC2
&lt;/h1&gt;

&lt;p&gt;Every cloud architecture begins with simplicity. The most natural first step is launching a single EC2 instance. A virtual machine that hosts both the application and the database. This setup closely mirrors a local development environment, where everything runs on the same machine. It’s quick to set up, low in cost, and easy to understand, which makes it ideal for prototypes or early-stage products.&lt;/p&gt;

&lt;p&gt;However, this convenience comes with limitations. The application has a single point of failure, no scalability, and limited security controls. It’s a great starting point, but clearly not a structure that can handle growth, traffic spikes, or production-grade reliability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k1blmj04eafxe2zlr1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k1blmj04eafxe2zlr1h.png" alt="Starting by an EC2" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Making It Reachable: DNS
&lt;/h1&gt;

&lt;p&gt;Once the EC2 instance is up and running, the next logical step is making it easily accessible. By default, access is through a public IP address, a temporary, hard-to-remember string of numbers. This isn’t sustainable for users or developers.&lt;/p&gt;

&lt;p&gt;Introducing DNS, typically through a service like Route 53, allows us to map a friendly domain name to the instance’s IP. This not only improves usability and branding, but also decouples the user-facing entry point from the underlying infrastructure. It becomes possible to change the underlying instance or later add layers like load balancers, without altering the public-facing domain. This step transforms a one-off server into a recognizable and maintainable endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjboripkqt1mccexqvn6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjboripkqt1mccexqvn6g.png" alt="Adding Route53" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Separating Concerns: Another EC2 for the Database
&lt;/h1&gt;

&lt;p&gt;As the application grows and usage increases, the limitations of running everything on a single instance become more apparent. Resource contention starts to affect performance, database queries slow down the app, and app crashes can impact data availability.&lt;/p&gt;

&lt;p&gt;The natural next move is to separate responsibilities by provisioning a second EC2 instance dedicated to the database. This separation improves stability and resource management. The application server can now be optimized for web traffic, while the database server is tuned for storage and queries.&lt;/p&gt;

&lt;p&gt;Although still manually managed, this step introduces a foundational architectural principle: separation of concerns, which enables more flexibility and paves the way for future scaling and specialization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7806khcvpwyavkq1pyl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7806khcvpwyavkq1pyl9.png" alt="Adding an Ec2 for database" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Managed is Better: Migrating to RDS
&lt;/h1&gt;

&lt;p&gt;Maintaining a database on an EC2 instance introduces several operational challenges, backups must be scripted, monitoring is manual, failover is complex, and scaling often involves downtime. As the architecture matures, offloading this responsibility becomes a priority.&lt;/p&gt;

&lt;p&gt;This is where Amazon RDS (Relational Database Service) comes into play. It offers a fully managed solution for databases like MySQL, PostgreSQL, or Aurora. With built-in backups, patching, high availability (via Multi-AZ), and metrics out of the box, RDS removes much of the operational burden.&lt;/p&gt;

&lt;p&gt;By migrating to RDS, the architecture takes a significant leap toward resilience and maintainability. It frees developers from low-level infrastructure tasks and ensures the data layer is better prepared for growth and reliability expectations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flnn9ho9pxakjm4rik2ex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flnn9ho9pxakjm4rik2ex.png" alt="Migrating to RDS" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Scaling Up: Vertical Scaling
&lt;/h1&gt;

&lt;p&gt;As traffic and demand on the application increase, the initial EC2 instance may begin to show signs of strain. Response times grow, CPU usage spikes, and memory may become a bottleneck. The quickest and most straightforward response is vertical scaling: Upgrading the EC2 instance to a more powerful type with more CPU, memory, or network capacity.&lt;/p&gt;

&lt;p&gt;This approach offers immediate performance gains without needing to change the application or infrastructure logic. It’s often the first form of scaling attempted because it's simple to implement and requires minimal architectural changes.&lt;/p&gt;

&lt;p&gt;However, vertical scaling has limits. There’s a ceiling to how large a single instance can grow, and it still represents a single point of failure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnjd54rgi107apkxterx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnjd54rgi107apkxterx.png" alt="Vertical Scaling" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  6. Scaling Out: Add Another EC2
&lt;/h1&gt;

&lt;p&gt;Once vertical scaling reaches its practical limits, the natural progression is to scale out by adding a second EC2 instance. This approach distributes traffic and workload across multiple servers, improving redundancy and overall performance.&lt;/p&gt;

&lt;p&gt;In this early stage of horizontal scaling, the setup often lacks a load balancer. Traffic may be routed manually, round-robin DNS may be used, or each server may handle specific tasks. While this offers basic fault tolerance and more compute power, it introduces complexity. Updates must be synchronized, configuration drift becomes a risk, and there’s no automatic traffic distribution.&lt;/p&gt;

&lt;p&gt;Still, this step marks an important shift. The architecture begins transitioning from a single-server mindset to a more distributed system. A necessary foundation for scalability and availability in future stages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dbkkcn4i08jswxtdpoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dbkkcn4i08jswxtdpoy.png" alt="Horizonal Scaling" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  7. Balancing Traffic: Load Balancer
&lt;/h1&gt;

&lt;p&gt;As more EC2 instances are added to support growing demand, managing how traffic reaches them becomes increasingly important. Manually directing traffic or relying on DNS tricks is brittle and hard to maintain. This is where a load balancer becomes essential.&lt;/p&gt;

&lt;p&gt;By introducing an Elastic Load Balancer (ELB), traffic is automatically distributed across healthy instances based on rules, health checks, and load. It abstracts the complexity of managing individual endpoints and provides a single entry point for clients.&lt;/p&gt;

&lt;p&gt;This step not only improves reliability and performance but also enables better deployment strategies like blue/green releases and zero-downtime rollouts. It marks a critical shift toward high availability, setting the stage for auto scaling, failover, and more sophisticated routing strategies in future phases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ixhdpatov7ws3ct4dji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ixhdpatov7ws3ct4dji.png" alt="Adding load Balancer" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  8. Secure the Traffic: ACM Certificates
&lt;/h1&gt;

&lt;p&gt;With a load balancer in place and the application now more publicly accessible, securing traffic becomes a top priority. Encrypted communication over HTTPS is essential not only for protecting user data but also for meeting compliance standards and improving trust.&lt;/p&gt;

&lt;p&gt;To achieve this, the architecture integrates SSL/TLS certificates using AWS Certificate Manager (ACM). These certificates can be easily provisioned and attached to the load balancer, enabling secure HTTPS connections without the need to manage keys or renewal cycles manually.&lt;/p&gt;

&lt;p&gt;Adding HTTPS at this stage ensures that all communication between clients and the application is encrypted. It also unlocks compatibility with modern browsers, APIs, and security-conscious platforms, reinforcing the application’s readiness for production-scale usage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyp3emm3171ns2zuh0bz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyp3emm3171ns2zuh0bz7.png" alt="Adding HTTPS" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  9. Strengthening Security: Private Subnets
&lt;/h1&gt;

&lt;p&gt;As the architecture becomes more public-facing and complex, protecting internal resources becomes critical. At this stage, the focus shifts to network-level security by restructuring the VPC and moving key components (such as EC2 instances and the RDS database) into private subnets.&lt;/p&gt;

&lt;p&gt;A private subnet ensures that these resources are no longer directly accessible from the internet. Only the load balancer, which remains in a public subnet, handles inbound traffic and forwards it internally. This significantly reduces the attack surface and aligns with best practices for cloud security.&lt;/p&gt;

&lt;p&gt;This move introduces the concept of a layered defense where not everything needs to be exposed, and access is granted only where absolutely necessary. It also sets up the foundation for introducing NAT gateways and more controlled outbound access in the next stage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7wc1pckgog8bo2w6oke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7wc1pckgog8bo2w6oke.png" alt="Moving to private subnets" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  10. Handling Traffic Spikes: Auto Scaling
&lt;/h1&gt;

&lt;p&gt;With multiple EC2 instances running behind a load balancer, the architecture is better equipped for availability, but still static in capacity. During traffic spikes, fixed instance counts may fall short, and during low-traffic periods, resources may sit idle.&lt;/p&gt;

&lt;p&gt;To address this, Auto Scaling Groups (ASG) are introduced. Auto scaling enables the system to dynamically adjust the number of EC2 instances based on defined metrics such as CPU usage, request volume, or custom CloudWatch alarms.&lt;/p&gt;

&lt;p&gt;This shift brings both cost efficiency and resilience. When traffic increases, new instances are automatically launched; when traffic drops, unused instances are terminated. Auto scaling also provides a safety net by replacing unhealthy instances automatically, reducing operational overhead and improving uptime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftl2wfi6awihfpzbrmq0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftl2wfi6awihfpzbrmq0p.png" alt="Adding auto scale" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  11. Outbound Access: NAT Gateway
&lt;/h1&gt;

&lt;p&gt;After moving compute resources into private subnets, a new challenge appears: these instances no longer have internet access. While this improves security, it also blocks necessary outbound communication like pulling OS updates, downloading packages, or calling external APIs.&lt;/p&gt;

&lt;p&gt;To solve this, a NAT Gateway is introduced. It acts as a secure bridge, allowing instances in private subnets to initiate outbound connections to the internet, while still remaining unreachable from the outside world.&lt;/p&gt;

&lt;p&gt;This step is a key piece of controlled connectivity. It balances security with operational needs, enabling critical outbound traffic without compromising the privacy and isolation of the internal network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2ajlm100h1hrhz45odf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2ajlm100h1hrhz45odf.png" alt="Adding Nat gateway" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  12. Persistent Storage: Multiple EBS Volumes
&lt;/h1&gt;

&lt;p&gt;As the application’s workload diversifies, so do its storage needs. Beyond the root volume of each EC2 instance, additional storage is often required for handling logs, file uploads, temporary data, or application-specific data partitions.&lt;/p&gt;

&lt;p&gt;To support this, the architecture begins attaching multiple EBS (Elastic Block Store) volumes to individual EC2 instances. EBS provides high-performance, persistent block storage that survives reboots and can be snapshot for backups or replication.&lt;/p&gt;

&lt;p&gt;This step improves data organization, performance tuning, and flexibility and allowing storage to scale independently of compute. However, it introduces management overhead and remains tied to specific availability zones and individual instances, which sets the stage for shared storage solutions in the next phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fei3t65ne0jtnc8h3ofc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fei3t65ne0jtnc8h3ofc0.png" alt="Adding EBS" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  13. Shared Storage: Moving to EFS
&lt;/h1&gt;

&lt;p&gt;Managing separate EBS volumes across multiple EC2 instances can become cumbersome, especially in horizontally scaled environments. When multiple instances need to access the same files for shared media, configurations, or synchronized processing. A new solution is needed.&lt;/p&gt;

&lt;p&gt;This is where Amazon EFS (Elastic File System) comes in. EFS provides a shared, scalable, and fully managed NFS file system that can be mounted simultaneously by multiple EC2 instances, regardless of their Availability Zone.&lt;/p&gt;

&lt;p&gt;By adopting EFS, the architecture gains shared storage with high availability and automatic scaling, removing the need to replicate files manually or rely on external sync processes. This simplifies development, reduces storage duplication, and prepares the system for workloads that require centralized, concurrent file access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6waiactaeqy60kynlo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6waiactaeqy60kynlo2.png" alt="Moving to EFS" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  14. Speeding Up Reads: Redis for Caching
&lt;/h1&gt;

&lt;p&gt;As traffic increases and the application becomes more data-intensive, repeated database queries can become a bottleneck, slowing down response times and increasing load on the RDS instance.&lt;/p&gt;

&lt;p&gt;To solve this, the architecture introduces a caching layer using Amazon ElastiCache with Redis. Redis is an in-memory key-value store that allows the application to quickly retrieve frequently accessed data such as session information, product listings, or user preferences without hitting the database every time.&lt;/p&gt;

&lt;p&gt;This step greatly enhances performance and scalability, reduces database pressure, and improves overall responsiveness. It also introduces a new layer in the system design: separating fast, ephemeral data from slower, persistent storage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgegz3zwc70jmq3egkydz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgegz3zwc70jmq3egkydz.png" alt="Adding Redis" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  15. Secure Access: Bastion Host
&lt;/h1&gt;

&lt;p&gt;As more resources are moved into private subnets for security, direct SSH access to EC2 instances is no longer possible from the outside world. While this is ideal from a security standpoint, administrators still need a secure way to access these instances for debugging, deployment, or maintenance.&lt;/p&gt;

&lt;p&gt;To enable this, a Bastion Host (also known as a jump box) is introduced. This is a single, tightly controlled EC2 instance placed in a public subnet, with strict access rules and hardened security settings. It acts as a gateway, allowing SSH access to private instances using internal networking.&lt;/p&gt;

&lt;p&gt;The Bastion Host reinforces least privilege access principles. Instead of opening up multiple EC2 instances to the internet, only one is exposed and access is logged, audited, and minimized. It becomes the controlled entry point into the private network layer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p2dxetrfgg35ks3crho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p2dxetrfgg35ks3crho.png" alt="Adding bastion host" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  16. Edge Caching: CloudFront CDN
&lt;/h1&gt;

&lt;p&gt;As the application gains a broader user base, especially across different geographic regions, latency becomes a noticeable concern. Serving static assets like images, stylesheets, scripts, or even cached HTML directly from EC2 or S3 can create slow load times for distant users.&lt;/p&gt;

&lt;p&gt;To address this, Amazon CloudFront, AWS’s content delivery network (CDN) is introduced. CloudFront caches content at edge locations around the world, delivering assets from the closest point to the end user.&lt;/p&gt;

&lt;p&gt;This dramatically improves performance, reduces bandwidth consumption, and lowers load on origin servers. It also enhances security, with built-in DDoS protection and support for signed URLs or geo-restriction. With CloudFront in place, the architecture becomes more globally responsive and efficient. A major milestone in user experience optimization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fej4593roilfm993knarq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fej4593roilfm993knarq.png" alt="Adding cloudfront" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  17. Object Storage: S3 Buckets
&lt;/h1&gt;

&lt;p&gt;As the system matures, storing static assets, like images, backups, logs, or user-generated content, directly on EC2 instances becomes inefficient and hard to manage. It increases storage pressure on compute resources and makes scaling more complicated.&lt;/p&gt;

&lt;p&gt;At this point, the architecture integrates Amazon S3 (Simple Storage Service), a highly durable, scalable object storage service. S3 is designed for storing virtually unlimited data with built-in redundancy, lifecycle policies, versioning, and fine-grained access controls.&lt;/p&gt;

&lt;p&gt;By offloading static files to S3, the application achieves better separation of concerns. EC2 instances focus solely on compute, while S3 becomes the system’s source of truth for file storage. When paired with CloudFront, S3 enables fast, global delivery of assets with low cost and minimal operational overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wwuc7xma5ukhvt28tor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wwuc7xma5ukhvt28tor.png" alt="Adding S3" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  18. Event-Driven: Lambda Triggered by S3
&lt;/h1&gt;

&lt;p&gt;With S3 now acting as the central hub for object storage, new opportunities arise to automate and streamline workflows. Instead of polling for changes or running scheduled jobs, the architecture can react automatically to events.&lt;/p&gt;

&lt;p&gt;This is where AWS Lambda comes into play. By configuring S3 to trigger Lambda functions on specific events, such as a new file upload or deletion, the system becomes event-driven. These functions can perform tasks like resizing images, generating thumbnails, scanning files, or indexing metadata, all without provisioning or managing servers.&lt;/p&gt;

&lt;p&gt;This step adds serverless automation to the architecture, reducing operational overhead while enabling real-time responsiveness. It also introduces loosely coupled components, a powerful pattern for building scalable and maintainable systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppiohiahenkny15a8u87.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fppiohiahenkny15a8u87.png" alt="Adding AWS Lambda" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  19. High Availability: Multi-AZ
&lt;/h1&gt;

&lt;p&gt;As reliability expectations rise, the architecture must be prepared to withstand failures at the infrastructure level, including entire data centers. AWS regions are made up of multiple Availability Zones (AZs), which are isolated locations with independent power, networking, and connectivity.&lt;/p&gt;

&lt;p&gt;To achieve high availability, the system is restructured to span multiple AZs. EC2 instances within the Auto Scaling Group are distributed across AZs, and RDS is configured for Multi-AZ deployment, which enables synchronous replication to a standby in a different zone.&lt;/p&gt;

&lt;p&gt;This design ensures that if one AZ goes down, the application and database remain operational through the others. It’s a critical move from single-point resilience to regional fault tolerance, minimizing downtime and improving overall system reliability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpd2wv6n6bjc57l8dm1cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpd2wv6n6bjc57l8dm1cw.png" alt="Multi AZ" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  20. Going Global: Multi-Region Deployment
&lt;/h1&gt;

&lt;p&gt;After achieving high availability within a region, the next step toward true resilience and scalability is multi-region deployment. This involves replicating critical parts of the infrastructure, application servers, databases, storage, and routing, across multiple AWS regions.&lt;/p&gt;

&lt;p&gt;Multi-region architecture improves disaster recovery, ensures low latency for global users, and provides regional failover in case of large-scale outages. DNS-level routing with Amazon Route 53 enables traffic to be directed based on latency, geography, or health checks, ensuring users always reach the closest and healthiest region.&lt;/p&gt;

&lt;p&gt;Implementing this step involves significant planning: handling data replication (often via cross-region S3 replication or multi-region databases), syncing infrastructure, and designing for eventual consistency. But it unlocks a level of resilience, performance, and global reach that’s essential for truly mission-critical or worldwide applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fty4xw6qbi6xdmkmgpcya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fty4xw6qbi6xdmkmgpcya.png" alt="Multi Region" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Thoughts: From Simple to Scalable
&lt;/h1&gt;

&lt;p&gt;This journey illustrates how an AWS architecture naturally evolves not through sudden redesigns, but through incremental, purposeful steps. Starting with a single EC2 instance, each stage solves a specific challenge: performance, availability, security, or scale.&lt;/p&gt;

&lt;p&gt;At every point, decisions are driven by real needs, adding a database instance, introducing DNS, securing traffic, automating scaling, or enabling global reach. What begins as a basic setup grows into a robust, distributed, and highly available system capable of serving users around the world.&lt;/p&gt;

&lt;p&gt;Not every application needs to reach the final stage right away. But understanding this progression helps teams plan ahead, avoid rework, and build systems that grow with their users and their business.&lt;/p&gt;

&lt;p&gt;Whether you're just starting or optimizing an existing deployment, AWS provides the flexibility to scale at your own pace, one architectural decision at a time.&lt;/p&gt;

&lt;p&gt;Start small. Grow smart.&lt;/p&gt;

&lt;p&gt;👉 If you found this helpful or want to discuss cloud architecture further, feel free to connect with me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>CI/CD for Beginners: Deploy a Static HTML Site to AWS EC2 with GitHub Actions</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Thu, 17 Jul 2025 22:00:27 +0000</pubDate>
      <link>https://forem.com/aws-builders/cicd-for-beginners-deploy-a-static-html-site-to-aws-ec2-with-github-actions-hfp</link>
      <guid>https://forem.com/aws-builders/cicd-for-beginners-deploy-a-static-html-site-to-aws-ec2-with-github-actions-hfp</guid>
      <description>&lt;p&gt;CI/CD (Continuous Integration and Continuous Deployment) is one of the most valuable skills a developer can learn today. In this guide, you'll learn how to set up a CI/CD pipeline that automatically deploys your HTML website to an AWS EC2 instance using GitHub Actions.&lt;/p&gt;

&lt;p&gt;By the end, every time you push code to GitHub, your website will update automatically!&lt;/p&gt;

&lt;h2&gt;
  
  
  🧰 Prerequisites
&lt;/h2&gt;

&lt;p&gt;Make sure you have the following ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Account&lt;/li&gt;
&lt;li&gt;A GitHub account&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛠️ Step 1: Create and Prepare Your EC2 Instance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔹 1.1 Launch the EC2 Instance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Head over to the EC2 Console.&lt;/li&gt;
&lt;li&gt;Click "Launch Instance".&lt;/li&gt;
&lt;li&gt;Fill in the form:

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;MyStaticSite&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AMI: Amazon Linux 2&lt;/li&gt;
&lt;li&gt;Instance Type: &lt;code&gt;t2.micro&lt;/code&gt; (Eligible for Free Tier)&lt;/li&gt;
&lt;li&gt;Key Pair: Create or use existing (download the &lt;code&gt;.pem&lt;/code&gt; file)&lt;/li&gt;
&lt;li&gt;Security Group: Allow:

&lt;ul&gt;
&lt;li&gt;SSH (port 22)&lt;/li&gt;
&lt;li&gt;HTTP (port 80)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Click "Launch".&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 1.2 Connect to Your Instance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Move the PEM file to a secure location (optional)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; ~/Downloads/your-key.pem ~/.ssh/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set correct permissions on the key file
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;400 ~/.ssh/your-key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;SSH into your EC2 instance
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/your-key.pem ec2-user@your-ec2-public-ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using Ubuntu, replace &lt;code&gt;ec2-user&lt;/code&gt; with &lt;code&gt;ubuntu&lt;/code&gt;.&lt;br&gt;
🔑 Replace:&lt;br&gt;
&lt;code&gt;your-key.pem&lt;/code&gt; → with your actual file name&lt;br&gt;
&lt;code&gt;your-ec2-public-ip&lt;/code&gt; → with your EC2 instance’s public IP or DNS (You can find them when you click on your instance in AWS console)&lt;/p&gt;
&lt;h3&gt;
  
  
  🔹 1.3 Install a Web Server and grant user privilege
&lt;/h3&gt;

&lt;p&gt;Amazon Linux 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; httpd
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start httpd
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;httpd
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;ec2-user /var/www/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ubuntu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apache2
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start apache2
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;apache2
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;ubuntu /var/www/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it: open your EC2 public IP in your browser — you should see the Apache test page.&lt;/p&gt;

&lt;h2&gt;
  
  
  📁 Step 2: Push Your HTML Code to GitHub
&lt;/h2&gt;

&lt;p&gt;Here's a simple structure for your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── index.html
└── .github
    └── workflows
        └── deploy.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;code&gt;index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello CI/CD&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;This was deployed using GitHub Actions!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit and push this code to your GitHub repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Step 3: Create GitHub Actions Workflow
&lt;/h2&gt;

&lt;p&gt;In your GitHub repo, create the file: &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to EC2&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt; &lt;span class="c1"&gt;# or 'master', based on your repo’s default branch&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Copy files via SCP&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/scp-action@v0.1.7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.EC2_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.EC2_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.EC2_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html"&lt;/span&gt;
          &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/www/html"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This GitHub Action will trigger on every push to the &lt;code&gt;main&lt;/code&gt; (or &lt;code&gt;master&lt;/code&gt;, depending on your repo) branch and securely copy your &lt;code&gt;index.html&lt;/code&gt; file to your EC2 instance using SSH.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔐 Step 4: Add Secrets to GitHub
&lt;/h2&gt;

&lt;p&gt;Go to your GitHub repository → Settings → Secrets and variables → Actions → New repository secret&lt;/p&gt;

&lt;p&gt;Add the following secrets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EC2_HOST&lt;/code&gt; → your EC2 public IP&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EC2_USER&lt;/code&gt; → usually &lt;code&gt;ec2-user&lt;/code&gt; (Amazon Linux) or &lt;code&gt;ubuntu&lt;/code&gt; (Ubuntu)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EC2_KEY&lt;/code&gt; → paste the contents of your private key &lt;code&gt;.pem&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Never commit &lt;code&gt;.pem&lt;/code&gt; files or expose them in public repos. GitHub Secrets are encrypted, but use IAM roles or session-based credentials for production setups.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🚀 Step 5: Push Your Code and Deploy
&lt;/h2&gt;

&lt;p&gt;Now commit and push to GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial CI/CD setup"&lt;/span&gt;
git push origin main &lt;span class="c"&gt;# or 'master' if your repo uses that&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head over to the Actions tab in your GitHub repo — you'll see the deploy job running.&lt;/p&gt;

&lt;p&gt;When it's finished, go to &lt;code&gt;http://&amp;lt;your-ec2-public-ip&amp;gt;&lt;/code&gt;. Your site should be live with the new HTML!&lt;/p&gt;

&lt;h2&gt;
  
  
  🎉 You're Done!
&lt;/h2&gt;

&lt;p&gt;Congrats! You’ve set up your first CI/CD pipeline using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions for automation&lt;/li&gt;
&lt;li&gt;EC2 as your deployment target&lt;/li&gt;
&lt;li&gt;SCP/SSH for secure transfer
Now, anytime you update your HTML and push to &lt;code&gt;main&lt;/code&gt; (or &lt;code&gt;master&lt;/code&gt;), your changes go live automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 What Happens Behind the Scenes
&lt;/h2&gt;

&lt;p&gt;Let’s break down what’s really going on when you push your code and GitHub Actions deploys it to EC2:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GitHub Detects a Push to main

&lt;ul&gt;
&lt;li&gt;GitHub watches for changes to your repository.&lt;/li&gt;
&lt;li&gt;When you push to the main branch, it checks .github/workflows/deploy.yml.&lt;/li&gt;
&lt;li&gt;This file tells GitHub: “Hey, run this workflow when there’s a push.”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Actions Spins Up a Runner

&lt;ul&gt;
&lt;li&gt;GitHub spins up a temporary virtual machine (Ubuntu in our case).&lt;/li&gt;
&lt;li&gt;This is where your workflow steps run.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Your Code Is Cloned

&lt;ul&gt;
&lt;li&gt;The actions/checkout step grabs your repo’s latest code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Secure Copy (SCP) Sends Files to EC2

&lt;ul&gt;
&lt;li&gt;The appleboy/scp-action uses scp under the hood.&lt;/li&gt;
&lt;li&gt;It connects to your EC2 instance using your SSH key (EC2_KEY) and username.&lt;/li&gt;
&lt;li&gt;Then it uploads your index.html to the EC2 server — typically in /var/www/html.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;EC2 Serves the Updated HTML

&lt;ul&gt;
&lt;li&gt;Your Apache or Nginx web server is already running on EC2.&lt;/li&gt;
&lt;li&gt;It’s watching the /var/www/html directory.&lt;/li&gt;
&lt;li&gt;When the new index.html is uploaded, it automatically starts serving the new version.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🔹 Local equivalent:
&lt;/h3&gt;

&lt;p&gt;Let’s look at what you’d do manually without CI/CD, so you can better appreciate what GitHub Actions automates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# GitHub Actions: checkout step&lt;/span&gt;
git clone https://github.com/your-username/your-repo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;your-repo

&lt;span class="c"&gt;# GitHub Actions: SCP upload step&lt;/span&gt;
scp &lt;span class="nt"&gt;-i&lt;/span&gt; your-key.pem index.html ec2-user@your-ec2-ip:/var/www/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔹 Summary of the Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub Repo  ---push---&amp;gt;  GitHub Actions
     ↓                         ↓
 deploy.yml              Ubuntu Runner
                               ↓
                       SSH into EC2 via key
                               ↓
                   Upload index.html via SCP
                               ↓
               Apache serves your site update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧠 Bonus Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add support for multiple environments (e.g., staging, production)&lt;/li&gt;
&lt;li&gt;Use Nginx instead of Apache for better performance&lt;/li&gt;
&lt;li&gt;Install SSL with Let’s Encrypt (Certbot)&lt;/li&gt;
&lt;li&gt;Automate EC2 provisioning with Terraform or CloudFormation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🙌 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;CI/CD doesn't have to be complicated. Starting with small projects like this builds the foundation for automating deployments in real-world applications.&lt;/p&gt;

&lt;p&gt;🌐 Got stuck? Drop a comment below and I’ll help you out — or connect with me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cicd</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Working With Legacy Code: From Survival to Mastery</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Tue, 08 Jul 2025 16:32:35 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/working-with-legacy-code-from-survival-to-mastery-5gae</link>
      <guid>https://forem.com/mhamadelitawi/working-with-legacy-code-from-survival-to-mastery-5gae</guid>
      <description>&lt;p&gt;You open a new project, and there it is. Thousands of lines of mystery code, no tests, strange names, A README last touched in 2016. Congratulations: you’ve met legacy code.&lt;/p&gt;

&lt;p&gt;But here’s the thing, &lt;strong&gt;legacy code isn’t a mess to clean up, It’s a story to uncover&lt;/strong&gt;. And more importantly, it’s the foundation of systems that power our world: banks, airlines, hospitals, governments.&lt;/p&gt;

&lt;p&gt;This guide will help you work with that kind of code. You’ll learn how to understand it, improve it safely, and feel more confident doing it. Because working with legacy systems isn’t just fixing things, it’s making a real difference&lt;/p&gt;

&lt;p&gt;Let’s get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1- What Is Legacy Code, Really?
&lt;/h2&gt;

&lt;p&gt;When people hear “legacy code,” they often think of old code, something written in an outdated language or built many years ago. But that’s only part of the picture. Legacy code isn’t just about age. It’s more about how the code feels to work with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s hard to understand.&lt;/li&gt;
&lt;li&gt;It doesn’t have tests.&lt;/li&gt;
&lt;li&gt;You’re afraid to change it because you might break something.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One definition that really sticks comes from Michael Feathers, in his classic book Working Effectively with Legacy Code, "Legacy code is code without tests". It might sound extreme, but it highlights the heart of the issue: if you don’t have tests, you can’t trust changes. And if you can’t trust changes, you can’t move fast or fix problems safely.&lt;/p&gt;

&lt;p&gt;Legacy code can be a five year old project built on outdated tools, a system the current team doesn’t fully understand, or even a rushed new feature, written last week, with no tests or documentation. It isn’t about the past, it’s about risk and your ability to safely work with and improve the code in front of you.&lt;/p&gt;

&lt;h2&gt;
  
  
  2- Why Legacy Code Exists
&lt;/h2&gt;

&lt;p&gt;It’s easy to look at legacy code and wonder, “Why would anyone write it this way?” But most legacy code didn’t start out bad. In fact, at the time it was written, it probably made perfect sense.&lt;/p&gt;

&lt;p&gt;Legacy code exists because things change technology, teams, priorities, and deadlines. Here are some common reasons it happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Business pressure:&lt;/strong&gt; Teams are often told to “just ship it,” even if that means skipping tests or long-term thinking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changing requirements:&lt;/strong&gt; What worked for version 1 may no longer fit version 10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tech moves fast:&lt;/strong&gt; Libraries, frameworks, and best practices evolve quickly. Code can feel outdated in just a few years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;People leave:&lt;/strong&gt; When the original developers move on, their context often goes with them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Blaming past developers doesn’t help. Most of the time, they were doing their best under pressure, just like we are today. The real value is in understanding why the code is the way it is so you can make it better, not just newer.&lt;/p&gt;

&lt;h2&gt;
  
  
  3- Common Challenges Developers Face
&lt;/h2&gt;

&lt;p&gt;Working with legacy code can feel frustrating, confusing, and sometimes even risky. If you’ve ever opened a file and thought, “I have no idea what this does, but I hope I don’t break it,” you’re not alone.&lt;/p&gt;

&lt;p&gt;Here are some of the most common challenges developers run into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No tests:&lt;/strong&gt; You can’t tell if your changes will break something. There’s no safety net.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fragile code:&lt;/strong&gt; Small changes in one place cause unexpected bugs in another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outdated tools and frameworks:&lt;/strong&gt; Old dependencies can block upgrades, slow you down, or even stop things from running on modern systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing or outdated documentation:&lt;/strong&gt; It’s hard to understand how the system works or why certain decisions were made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling and tangled logic:&lt;/strong&gt; Code is often written without clear boundaries, making it hard to isolate and improve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fear of touching “scary” parts:&lt;/strong&gt; Some areas are so fragile or complex that no one wants to be responsible for changing them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this can slow teams down, create stress, and make even small changes feel like high-stakes moves.&lt;/p&gt;

&lt;p&gt;But here’s the good news: these challenges are common and they’re solvable. The rest of this guide will show you how to handle them with confidence, care, and a bit of strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  4- Mindset Shift: How to Approach Legacy Code
&lt;/h2&gt;

&lt;p&gt;Before you write a single line of code, the most important thing you can change is your mindset.&lt;/p&gt;

&lt;p&gt;It’s easy to see legacy code as a burden or even as someone else’s mistake. But that mindset leads to frustration, blame, and burnout. Instead, try to see legacy code as an opportunity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An opportunity to learn how the system works.&lt;/li&gt;
&lt;li&gt;An opportunity to improve something that matters to real users.&lt;/li&gt;
&lt;li&gt;An opportunity to leave things better than you found them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The truth is, almost every developer, at some point, has written code that became legacy. Deadlines, business pressure, or missing knowledge these things happen to all of us.&lt;/p&gt;

&lt;p&gt;By approaching legacy code with curiosity instead of resentment, you’ll not only enjoy the work more, but you’ll also become a stronger, more thoughtful developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  5- First Steps When You Inherit a Legacy Codebase
&lt;/h2&gt;

&lt;p&gt;The first time you inherit a legacy codebase, it can feel overwhelming like being dropped in the middle of a maze without a map. But you don’t need to figure it all out at once.&lt;/p&gt;

&lt;p&gt;Here are simple first steps to help you get your footing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Get the code running:&lt;/strong&gt; Before changing anything, make sure you can build, run, and deploy the project safely (even in a local or test environment).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Look for a README or setup guide:&lt;/strong&gt; Even outdated notes can give you clues about how things are supposed to work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for existing tests:&lt;/strong&gt; Any kind of test unit, integration, or manual scripts, can help you understand the system’s behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trace the main flows:&lt;/strong&gt; Identify the key features or user actions and follow them through the code. This helps you spot where things really happen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the seams:&lt;/strong&gt; Seams are places in the code where you can safely make changes or add tests without affecting everything else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t need to understand every line or every module on day one. Start small. Focus on what you need to change or fix and build your understanding step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  6- Strategies for Working With and Improving Legacy Code
&lt;/h2&gt;

&lt;p&gt;Once you’ve found your footing, the next step is figuring out how to actually work with legacy code safely and effectively. The key is to make small, steady improvements without breaking what already works.&lt;/p&gt;

&lt;p&gt;Here are some proven strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add Characterization Tests:&lt;/strong&gt; Before changing code, write simple tests to capture what it currently does. This way, you’ll know if you accidentally break something.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply the Boy Scout Rule:&lt;/strong&gt; “Leave the code a little cleaner than you found it.” Even small improvements,renaming variables, deleting dead code add up over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor in Small Steps:&lt;/strong&gt; Don’t try to fix everything at once. Tackle one function, one module, or one bug at a time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate Where You Can:&lt;/strong&gt; Automated tests, linters, and static analysis tools give you confidence to make changes safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like cleaning an old house you don’t have to rebuild it from scratch to make it better. Fix what you touch. Improve what you need. Over time, the system becomes safer and easier to work with. It’s also important to communicate these improvements to non-technical stakeholders. Refactoring, adding tests, and paying down technical debt often don’t produce flashy new features, but they directly improve the team’s ability to move faster, reduce bugs, and avoid costly outages. When talking to business partners, frame these efforts in terms of reduced risk, improved delivery speed, and long-term cost savings, not just “cleaner code”.&lt;/p&gt;

&lt;h2&gt;
  
  
  7- Refactoring Patterns and Approaches
&lt;/h2&gt;

&lt;p&gt;Refactoring legacy code isn’t about jumping in blindly. There are tried-and-true patterns that help you improve old systems safely, step by step. Each pattern gives you a clear approach to modernizing without breaking things or falling into the “big rewrite” trap.&lt;/p&gt;

&lt;p&gt;Here are some of the most effective approaches you can use:&lt;/p&gt;

&lt;h3&gt;
  
  
  🌱 Strangler Fig Pattern
&lt;/h3&gt;

&lt;p&gt;This approach lets you gradually replace parts of a legacy system by building new components alongside the old ones. Over time, the new code takes over, and the old code is “strangled” and removed. Use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a full rewrite is too risky or expensive.&lt;/li&gt;
&lt;li&gt;When you need to modernize parts of the system while keeping it running.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
You want to replace an old checkout system. Instead of rewriting everything, you build a new checkout module and route some traffic to it. Little by little, you shift more users to the new code until the old system can be retired.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffc6gpss41wi3gq6gza41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffc6gpss41wi3gq6gza41.png" alt="Phases of the Strangler Fig Migration" width="800" height="764"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To visualize how the Strangler Fig Pattern works in practice, here’s a breakdown of its stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stage 0 - Legacy System Only:&lt;/strong&gt;
At the beginning, your entire application is the legacy system. All traffic, business logic, and features live inside it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 1 - Introduce Strangler Facade:&lt;/strong&gt;
A Strangler Facade is added as a unified entry point (often an API gateway, proxy, or routing layer) that sits in front of the legacy system. It prepares the ground for gradual change without touching the legacy code immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 2 - Introduce New Component(s):&lt;/strong&gt;
New features or modules are developed separately and integrated through the Strangler Facade. Some traffic is now routed to the new components while the rest still flows through the legacy system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 3 - Expand New System, Reduce Legacy:&lt;/strong&gt;
More functionality is moved to the new system over time. The legacy system starts to shrink as new code handles more requests and business logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 4 - Retire Legacy System:&lt;/strong&gt;
Once enough functionality has been migrated, the legacy system can be safely decommissioned. The new system now handles all operations but still passes through the facade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 5 - Retire Strangler Facade:&lt;/strong&gt;
With the legacy system gone, the facade is no longer necessary. It can be removed, leaving a clean, fully modernized system.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  🔀 Branch by Abstraction
&lt;/h3&gt;

&lt;p&gt;This pattern lets you change behavior behind an interface or abstraction without breaking the existing code. Both the old and new versions can coexist side by side until the new code is stable, tested, and ready for full rollout.&lt;/p&gt;

&lt;p&gt;It’s especially useful when you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change deep business logic without disrupting current features.&lt;/li&gt;
&lt;li&gt;Control rollout in small, reversible steps rather than risky big-bang deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95jj9e5f0ih57h2p6gp7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95jj9e5f0ih57h2p6gp7.png" alt="Coexisting Legacy and New Code Using an Abstraction Layer" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this approach, the Consumer interacts only with an Abstraction Layer, a stable interface that hides the underlying implementation. Behind that abstraction, both the Legacy Component and the New Component are available. The abstraction decides which version to use, making it possible to switch between them easily or even run them in parallel.&lt;/p&gt;

&lt;p&gt;For example, imagine you need to replace the shipping cost calculation in an e-commerce system. You create an interface—say, ShippingCalculator—and plug in both the old and new implementations. The consumer code doesn’t change. Once the new version is proven, you simply switch over and remove the old code safely.&lt;/p&gt;

&lt;p&gt;This method allows you to write, test, and deploy the new logic without impacting users, and it gives you the flexibility to roll back quickly if needed.&lt;/p&gt;
&lt;h3&gt;
  
  
  🧩 Modularization
&lt;/h3&gt;

&lt;p&gt;Large, messy systems often have everything bundled together. Modularization means splitting the code into smaller, focused modules or services that are easier to understand, test, and maintain. Use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a single file, class, or service is doing too many things.&lt;/li&gt;
&lt;li&gt;When you want to isolate and improve one part of the system at a time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
A legacy app has all the user management, payment processing, and notifications tangled together in one place. You start by moving the payment logic into its own module, with clear inputs and outputs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8h2ul2plq7yt086a2u8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8h2ul2plq7yt086a2u8.png" alt="Modularization" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  🔗 Service Extraction
&lt;/h3&gt;

&lt;p&gt;When a part of the code handles a distinct responsibility, you can extract it into a standalone service or microservice. This reduces complexity and allows teams to scale or evolve parts of the system independently. Use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a piece of functionality has grown too large or different from the core app.&lt;/li&gt;
&lt;li&gt;When you need to scale or deploy part of the system separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
The legacy system handles payments, users, and reporting. You extract the payment feature into its own microservice with clean boundaries, separate deployments, and its own tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bvfawl4s37e0emtzbil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bvfawl4s37e0emtzbil.png" alt="Monolithic Vs Microservice Architecture" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  🪤 Anti-Corruption Layer (Gateway)
&lt;/h3&gt;

&lt;p&gt;An Anti-Corruption Layer (ACL) is a way to protect your new code from the mess of old legacy systems. It works like a translator or a safety barrier between your clean, modern code and the outdated systems you still depend on.&lt;/p&gt;

&lt;p&gt;Instead of letting your new code call the legacy code directly which could pull in confusing designs, bad habits, or technical debt , you build an abstraction layer. This layer handles all communication and makes sure the old system’s weirdness doesn’t leak into your new system.&lt;/p&gt;

&lt;p&gt;Use It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you’re building new features that still depend on old, messy systems you can’t replace right away.&lt;/li&gt;
&lt;li&gt;When you want to keep your new code clean, simple, and free from legacy mistakes.&lt;/li&gt;
&lt;li&gt;When you need to control how two very different systems talk to each other.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mx1y0h8pmsyg6yuqbzv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mx1y0h8pmsyg6yuqbzv.png" alt="Isolating Legacy Systems with an Anti-Corruption Layer" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
Let’s say you’re building a new analytics dashboard, but the data lives in an old system that’s hard to work with. Instead of letting your dashboard talk to the legacy system directly, you create an Anti-Corruption Layer—a new service that handles everything in between. This layer talks to the old system, cleans up the data, and hands it to your new code in a safe, modern format.&lt;/p&gt;
&lt;h3&gt;
  
  
  🔄 Event-Driven Refactoring
&lt;/h3&gt;

&lt;p&gt;Instead of calling other parts of the system directly, you can use events to decouple components. This reduces dependencies and makes future changes safer. Use it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When multiple parts of the system need to react to the same action.&lt;/li&gt;
&lt;li&gt;When you need to scale or change behavior without rewriting everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5g08mgc1avlahy742zf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5g08mgc1avlahy742zf.png" alt="Event Driven Architecture" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
Right now, after an order is placed, the system directly calls the payment, inventory, and email services. You change this to publish an OrderPlaced event. Other parts of the system subscribe to that event and act independently.&lt;/p&gt;
&lt;h2&gt;
  
  
  8- Tools That Help Tame Legacy Code
&lt;/h2&gt;

&lt;p&gt;Legacy code can feel overwhelming, but the right tools can make a big difference. They help you understand, clean, and protect your codebase as you work. These tools won’t fix legacy problems by themselves, but they give you visibility, safety, and automation, the essentials for working safely and confidently in any legacy system.&lt;/p&gt;

&lt;p&gt;Here are some useful categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linters:&lt;/strong&gt; Tools like ESLint, RuboCop, or Pylint help enforce coding standards and catch obvious issues early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Coverage Tools:&lt;/strong&gt; Tools like Istanbul (JavaScript), JaCoCo (Java), or Coverage.py (Python) show you which parts of the code are tested and which aren’t.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static Analyzers:&lt;/strong&gt; Tools like SonarQube, CodeClimate, or DeepSource help identify hidden bugs, security issues, and code smells.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Checkers:&lt;/strong&gt; Tools like Dependabot or npm audit help you spot outdated or vulnerable libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools don’t replace good thinking, but they give you visibility and safety, two things every legacy project needs.&lt;/p&gt;
&lt;h2&gt;
  
  
  9- Testing Legacy Code
&lt;/h2&gt;

&lt;p&gt;One of the hardest parts of working with legacy code is the constant fear of breaking something. The best way to fight that fear is to add tests before you make any big changes.&lt;/p&gt;

&lt;p&gt;The problem is, legacy code often isn’t written in a way that makes testing easy. It might be tangled, tightly coupled, or full of side effects. But with the right techniques, you can still add valuable tests that give you confidence to move forward.&lt;/p&gt;

&lt;p&gt;Here’s how to do it step by step:&lt;/p&gt;
&lt;h3&gt;
  
  
  🎯Characterization Tests
&lt;/h3&gt;

&lt;p&gt;Tests that capture what the code currently does, even if that behavior is strange or incorrect. It gives you a safety net when refactoring because you’ll know if behavior accidentally changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testCharacterizeCalculateDiscount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DiscountCalculator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;calculate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// We capture the current behavior even if it's wrong.&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;85.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you safely refactor the method later, knowing you won’t accidentally change its current behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  📸Golden Master Testing
&lt;/h3&gt;

&lt;p&gt;Run the system, capture the current output, and use that as the baseline for future comparisons. It’s perfect for legacy code that’s too complex or messy to unit test easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testInvoiceGenerationGoldenMaster&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InvoiceService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateInvoice&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampleOrder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/test/resources/golden_master_invoice.txt"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you’re comparing the full output like a report or document to a saved correct version.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✂️Test Seams
&lt;/h3&gt;

&lt;p&gt;A seam is a place where you can control behavior for testing like injecting dependencies instead of hardcoding them. It lets you add tests without rewriting the entire system.&lt;/p&gt;

&lt;p&gt;Before (no seam):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After (with seam):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paymentService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in your test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testProcessPaymentWithFakeService&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="n"&gt;fakeService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockito&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fakeService&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampleOrder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fakeService&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampleOrder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By injecting the dependency, you can now test safely without hitting real services.&lt;/p&gt;

&lt;h3&gt;
  
  
  📝Approval Testing
&lt;/h3&gt;

&lt;p&gt;A technique where you compare outputs to an “approved” version and check for unintended changes. It’s useful for complex outputs like HTML, reports, or documents where exact values can vary.&lt;/p&gt;

&lt;p&gt;Java Example (Using ApprovalTests library):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testGenerateInvoiceHtml&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InvoiceService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateInvoiceHtml&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampleOrder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;Approvals&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tools like &lt;a href="https://github.com/approvals/ApprovalTests.Java" rel="noopener noreferrer"&gt;ApprovalTests &lt;/a&gt;for Java handle storing the approved result and highlighting any differences automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  10- When to Refactor vs. Rewrite
&lt;/h2&gt;

&lt;p&gt;At some point, every team working with legacy code faces the same question:&lt;br&gt;
Should we fix what we have, or should we start over and build something new?&lt;/p&gt;

&lt;p&gt;Starting fresh can sound exciting new tools, cleaner code, no old mess. But in reality, the choice between refactoring and rewriting is rarely simple. It’s even harder to convince the business that either option is worth the time and cost, especially when the current system is running, making money, and keeping customers happy. The usual thinking is:&lt;br&gt;
“If it works, why change it?”&lt;/p&gt;

&lt;p&gt;That’s why it’s so important to know when small, steady refactoring is the right move and when a full rewrite is truly the better path. A good rule of thumb is to always ask:  &lt;strong&gt;“What business problem are we solving by rewriting this?”&lt;/strong&gt;&lt;br&gt;
If you can’t answer that clearly, you’re probably better off refactoring. Also you need to explain these choices in ways that non-technical people can understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 When to Refactor
&lt;/h3&gt;

&lt;p&gt;Refactoring means improving the existing codebase in small, controlled steps. You keep the core system running while making it easier to work with over time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The system still works but is messy, hard to change, or fragile.&lt;/li&gt;
&lt;li&gt;The business relies on it and cannot afford downtime or risky overhauls.&lt;/li&gt;
&lt;li&gt;You need to fix bugs, add features, or improve performance gradually.&lt;/li&gt;
&lt;li&gt;You want to lower risk while keeping the value the system already provides.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
A busy e-commerce platform built 7 years ago still processes orders daily. Instead of rewriting, you add tests, break apart monolithic classes, and improve specific pain points while keeping the system live.&lt;/p&gt;

&lt;p&gt;Briefly, Refactoring is often the safest path especially when the software is actively used and profitable.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔄 When to Rewrite
&lt;/h3&gt;

&lt;p&gt;Rewriting means starting fresh building a new system from the ground up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The technology is outdated and actively blocks progress (e.g., can’t scale, can’t hire developers, can’t integrate with modern tools).&lt;/li&gt;
&lt;li&gt;The architecture no longer fits the business model or growth plans.&lt;/li&gt;
&lt;li&gt;The cost of maintaining or adding features has become higher than starting over.&lt;/li&gt;
&lt;li&gt;The team has the time, resources, and business support to do it right.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
A financial institution is running mission-critical software on COBOL with no developers left to maintain it. After careful planning, they invest in rewriting the system using modern languages and cloud infrastructure.&lt;/p&gt;

&lt;p&gt;Briefly, Rewrites can succeed but only with strong business buy-in, careful planning, and acceptance of short-term disruption.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚖️ The Tough Truth: It’s Hard to Convince the Business
&lt;/h3&gt;

&lt;p&gt;Most businesses don’t easily invest in technical refactoring or rewrites especially when the current system works, generates revenue, and hasn’t caused visible pain.&lt;/p&gt;

&lt;p&gt;As engineers, we see technical debt the friction that slows us down, increases risks, and raises maintenance costs. The business, however, sees working software and may not immediately feel the pain. That’s why it’s crucial to frame technical decisions in business terms: How will refactoring help us ship faster? How will it reduce incidents, improve customer satisfaction, or lower costs over time? Reframe “tech debt” as a business enabler, not just an engineering concern. That’s why you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tie technical work to business outcomes: speed, stability, new features, reduced costs.&lt;/li&gt;
&lt;li&gt;Show small wins from refactoring before asking for major investments.&lt;/li&gt;
&lt;li&gt;Be ready to explain the risks of inaction (security issues, scaling problems, talent gaps).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11- Security, Compliance &amp;amp; Risks in Legacy Code
&lt;/h2&gt;

&lt;p&gt;Legacy code isn’t just about messy methods or outdated frameworks, it can also hide serious security risks and compliance issues that put the entire business at risk.&lt;/p&gt;

&lt;p&gt;Many older systems were built in a time when security wasn’t as critical, regulations were different, and common best practices simply didn’t exist. That’s why legacy systems often carry hidden dangers that are easy to overlook until something goes wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚨 Common Risks You’ll Find in Legacy Code
&lt;/h3&gt;

&lt;p&gt;Even well-running legacy systems can hide risks that expose the business to security threats and compliance issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardcoded passwords or API keys left inside the code.&lt;/li&gt;
&lt;li&gt;Lack of encryption for sensitive data (user info, payments, medical records).&lt;/li&gt;
&lt;li&gt;Missing input validation, which opens the door to SQL injection or cross-site scripting (XSS).&lt;/li&gt;
&lt;li&gt;Outdated libraries with known security flaws.&lt;/li&gt;
&lt;li&gt;No audit logs or traceability, making it hard to detect or investigate problems.&lt;/li&gt;
&lt;li&gt;Non-compliance with current security laws or industry standards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ How to Handle Security and Compliance in Legacy Systems
&lt;/h3&gt;

&lt;p&gt;Security in legacy code can’t always be fixed overnight. But with a clear plan, you can reduce risks step by step while keeping the system running.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Assess and Prioritize:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use security scans and static analysis tools (e.g., OWASP ZAP, Snyk, SonarQube).&lt;/li&gt;
&lt;li&gt;Check for vulnerable dependencies and outdated libraries.&lt;/li&gt;
&lt;li&gt;Make a list of regulatory requirements that apply to your system.&lt;/li&gt;
&lt;li&gt;Focus first on fixing issues that carry the highest risk to the business.&lt;/li&gt;
&lt;li&gt;Even small wins like removing hardcoded secrets add up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Isolate and Contain:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify risky parts of the system (handling payments, personal data, etc.).&lt;/li&gt;
&lt;li&gt;Add boundaries using APIs, service layers, or access controls to contain risks.&lt;/li&gt;
&lt;li&gt;Apply patterns like the Strangler Fig to move sensitive features into safer, modern code over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Fix and Improve Incrementally:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix small security gaps as you touch the code (the Boy Scout Rule).&lt;/li&gt;
&lt;li&gt;Add security checks into your CI/CD pipeline to prevent new problems.&lt;/li&gt;
&lt;li&gt;Introduce basic logging and monitoring to spot issues early.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Communicate with the Business:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explain security risks in plain language, not just “technical debt” but real risks: data loss, legal penalties, customer trust.&lt;/li&gt;
&lt;li&gt;Show how small improvements protect revenue and reputation without needing a costly rebuild.&lt;/li&gt;
&lt;li&gt;Document known risks if you can’t fix everything right away.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  12- Code Archaeology: Digging Into the Past
&lt;/h2&gt;

&lt;p&gt;Working with legacy code often feels less like modern software development and more like archaeology, digging through layers of decisions, patches, and quick fixes made over the years.&lt;/p&gt;

&lt;p&gt;To improve legacy code safely, you first need to understand its history: why it was written the way it was, what decisions shaped it, and which parts are still important today.&lt;/p&gt;

&lt;p&gt;This process is sometimes called code archaeology and it's a powerful skill that can save you from costly mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏺 How to Do Code Archaeology
&lt;/h3&gt;

&lt;p&gt;Here’s how you can dig into the past and make sense of legacy code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Version Control as Your Map:&lt;/strong&gt; Tools like &lt;code&gt;git log&lt;/code&gt;, &lt;code&gt;git blame&lt;/code&gt;, and pull request history can tell you who changed what, when, and why. Look for patterns: recurring bug fixes, old TODOs, unexplained changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check the Changelogs and Documentation (if any):&lt;/strong&gt; Sometimes old release notes, wikis, or even comments in the code hold valuable clues. Business context hidden in these notes can explain Weird workarounds, Strange decisions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Talk to the “Historians”:&lt;/strong&gt; If possible, find teammates (past or present) who worked on the system. Even short conversations can reveal decisions that aren’t written down anywhere&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Identify Code Tombs and Dead Ends:&lt;/strong&gt; Look for code that no longer serves any real purpose, leftovers from old features, forgotten experiments, half-removed functionality. Remove them carefully or quarantine them to reduce confusion.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  13- Legacy Systems Across Contexts
&lt;/h2&gt;

&lt;p&gt;Legacy code looks different depending on the industry, technology, and business environment. Each type of system comes with its own challenges and the way you approach it should match its context.&lt;br&gt;
Here’s a quick comparison to help you understand the differences:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Common Traits&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Challenges&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Suggested Approach&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔌 &lt;strong&gt;Embedded Systems&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Low-level languages (C/C++), runs on hardware, long lifespan&lt;/td&gt;
&lt;td&gt;Hard to update, expensive to test, safety-critical&lt;/td&gt;
&lt;td&gt;Use emulators, introduce test seams, prioritize stability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏦 &lt;strong&gt;Finance, Insurance, Government&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;COBOL, mainframes, large relational databases, strict regulations&lt;/td&gt;
&lt;td&gt;Downtime not acceptable, skills shortage, compliance-heavy&lt;/td&gt;
&lt;td&gt;Gradual modernization, API layers, risk-managed refactoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🌐 &lt;strong&gt;Web &amp;amp; Enterprise Applications&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Older stacks (.NET, PHP, Java, Rails), monoliths, inconsistent code quality&lt;/td&gt;
&lt;td&gt;Technical debt, slow delivery, fragile deployments&lt;/td&gt;
&lt;td&gt;Modularization, microservices, automated testing, CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🏥 &lt;strong&gt;Healthcare, Aerospace, Critical Systems&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;High safety, strict regulations, tightly integrated software &amp;amp; hardware&lt;/td&gt;
&lt;td&gt;Change is slow, failures can have severe consequences&lt;/td&gt;
&lt;td&gt;Focus on risk management, strong testing, traceability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  14- Team &amp;amp; Organizational Culture Around Legacy Code
&lt;/h2&gt;

&lt;p&gt;Legacy code isn’t just a technical problem it’s often a team and culture problem too.&lt;/p&gt;

&lt;p&gt;Without the right mindset, teams fall into habits like blaming past developers, avoiding the code, or accepting that “it’s just how things are.” This leads to frustration, fear, and a system that only gets worse over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚫 Unhealthy Legacy Code Cultures:
&lt;/h3&gt;

&lt;p&gt;Some team habits can make legacy code even harder to deal with. Here are the warning signs to watch for.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;That’s not my problem:&lt;/strong&gt; no one takes ownership.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fear of touching the code:&lt;/strong&gt; change only happens when something breaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No time for refactoring:&lt;/strong&gt; teams are stuck in “just ship it” mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blaming the past&lt;/strong&gt;: instead of improving the present.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧑‍🤝‍🧑 Healthy Legacy Code Cultures:
&lt;/h3&gt;

&lt;p&gt;A healthy culture is just as important as good code when working with legacy systems. Here are some key traits of teams that thrive in legacy environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared Ownership:&lt;/strong&gt; Everyone feels responsible for the health of the codebase not just one person or team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Psychological Safety:&lt;/strong&gt; People feel safe to suggest improvements without fear of blame or judgment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small Wins Matter:&lt;/strong&gt; Teams celebrate gradual improvements even deleting one dead function is a win.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Learning:&lt;/strong&gt; The team values learning from the past, not shaming it. They take time to reflect on why legacy decisions were made and how to improve them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Debt as a Business Concern:&lt;/strong&gt; Leadership understands that technical debt affects speed, stability, and innovation. It's not “just a developer problem.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A key part of building this culture is teaching teams to regularly share progress on legacy improvements even when the work is invisible. This could mean showing increased test coverage, fewer production incidents, or faster release cycles. When technical teams learn to translate their wins into business outcomes, leadership is far more likely to support ongoing improvement efforts. Look for opportunities to share these small wins during retrospectives, sprint demos, and planning meetings. It helps make invisible progress visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ How to Build a Better Culture Around Legacy Code:
&lt;/h3&gt;

&lt;p&gt;A healthy approach to legacy code starts with the right team mindset, Here’s how to build it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encourage small, constant improvements:&lt;/strong&gt; Apply the Boy Scout Rule leave things better than you found them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lead by example:&lt;/strong&gt; Senior engineers and tech leads should model healthy behaviors writing tests, refactoring safely, and sharing knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talk openly about technical debt:&lt;/strong&gt; Make it part of sprint planning, retrospectives, and roadmaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair programming and code reviews:&lt;/strong&gt; Use them not just to check code but to transfer legacy knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Celebrate wins:&lt;/strong&gt; Removing 100 lines of useless code is just as valuable as shipping a shiny new feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  15- Future-Proofing: Writing Today’s Code to Avoid Tomorrow’s Legacy
&lt;/h2&gt;

&lt;p&gt;Every piece of code we write today is a potential legacy system of tomorrow. The truth is, no matter how modern your tools or frameworks are, code can become legacy faster than you think especially if it's hard to understand, test, or change.&lt;/p&gt;

&lt;p&gt;The good news? You can’t predict the future, but you can make choices today that make life easier for the next team including your future self.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏗️ Practical Tips for Future-Proofing Your Code:
&lt;/h3&gt;

&lt;p&gt;For clean code that stays adaptable and easy to maintain over time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write Tests Early and Often:&lt;/strong&gt; Code without tests becomes fragile faster than you expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep It Simple:&lt;/strong&gt; Avoid over-engineering. Clear, readable code lasts longer than “clever” solutions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Decisions, Not Just Code:&lt;/strong&gt; Explain why things are done a certain way not just how.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for Change:&lt;/strong&gt; Small, loosely coupled components are easier to upgrade, replace, or retire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name Things Clearly:&lt;/strong&gt; Future developers (even you) should understand what something does without guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leave Good Traces:&lt;/strong&gt; Update READMEs, commit messages, and diagrams when you change something meaningful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally remember the goal isn’t perfection. It’s writing code that is easy to read, safe to change, and kind to those who come after you.. That’s how you avoid creating the legacy code nightmares of the future. And don’t forget to communicate these choices, leaving good documentation and sharing your reasoning helps future teams (and stakeholders) understand why things were done a certain way.&lt;/p&gt;

&lt;h2&gt;
  
  
  16- Conclusion: Legacy Code as an Inheritance
&lt;/h2&gt;

&lt;p&gt;Working with legacy code isn’t a punishment it’s a fundamental part of being a professional software engineer. Beneath every tangled method and outdated framework lies something important: the story of how systems came to be, how businesses grew, and how real-world needs shaped the software we inherit today.&lt;/p&gt;

&lt;p&gt;It’s easy to look at legacy code with frustration or even dread. But the truth is, legacy systems are the backbone of industries that impact millions of lives. Banks, airlines, hospitals, governments they all run on code that someone, somewhere, once wrote under pressure, with the best intentions and the constraints of their time.&lt;/p&gt;

&lt;p&gt;The best developers aren’t just those who build new, shiny things. They’re the ones who can safely, thoughtfully, and patiently improve what already exists. They know that real impact often comes not from starting over, but from making steady, careful changes that honor the systems people rely on every day.&lt;/p&gt;

&lt;p&gt;If there’s one thing I hope you take away, it’s this: legacy code is not your enemy it’s your inheritance. And the skills you build working with it are the same skills that will make you a more resilient, thoughtful, and valuable engineer for years to come.&lt;/p&gt;

&lt;p&gt;If you’d like to explore these ideas further, here are some excellent books that have stood the test of time, just like the systems we work on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working Effectively with Legacy Code - Michael Feathers&lt;/li&gt;
&lt;li&gt;Refactoring: Improving the Design of Existing Code - Martin Fowler&lt;/li&gt;
&lt;li&gt;Clean Code - Robert C. Martin&lt;/li&gt;
&lt;li&gt;Building Evolutionary Architectures - Neal Ford, Rebecca Parsons, Patrick Kua&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, the legacy code you improve today might just be the foundation that supports someone else’s future success tomorrow.&lt;/p&gt;

&lt;p&gt;🌐 For more tech insights, you can find me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>refactoring</category>
      <category>softwareengineering</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Leaderboard Illusion: Is Your Ai Model Smart or Just Well-Studied?</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Sat, 28 Jun 2025 13:43:09 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/the-leaderboard-illusion-is-your-model-smart-or-just-well-studied-1ndo</link>
      <guid>https://forem.com/mhamadelitawi/the-leaderboard-illusion-is-your-model-smart-or-just-well-studied-1ndo</guid>
      <description>&lt;p&gt;Leaderboards are everywhere in AI these days. They help us compare models, track progress, and decide which ones are worth our time and resources. But sometimes, a model's top score might raise an eyebrow—almost like it knew the answers ahead of time.&lt;/p&gt;

&lt;p&gt;It’s easy to assume the highest-ranked models are the smartest or most capable. But in reality, there’s a subtle issue that can throw these rankings off. And while it might sound like cutting corners, it’s not always that simple—or even wrong.&lt;/p&gt;

&lt;p&gt;In this article, we’ll take a closer look at how this issue impacts model evaluations, why it’s more common than you might think, and how, when handled carefully, it can actually make models more useful in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 What Is Data Contamination?
&lt;/h2&gt;

&lt;p&gt;Data contamination in AI refers to situations where information that shouldn't be present during model training accidentally influences the learning process, leading to misleadingly good performance and poor generalization.&lt;/p&gt;

&lt;p&gt;In this article, we focus specifically on one type of data contamination: when a model is trained on the same data that’s later used to evaluate it. &lt;/p&gt;

&lt;p&gt;Think of training an AI model like preparing a student for an exam.&lt;/p&gt;

&lt;p&gt;Now imagine if that student had access to the exact exam questions during their study sessions. On test day, they ace the exam—not because they deeply understand the material, but because they memorized the answers.&lt;/p&gt;

&lt;p&gt;This is what data contamination means in AI:&lt;br&gt;
A model is evaluated on the same data it saw during training, so the high score might just reflect memorization, not true skill or reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  📉 Why Is It a Problem?
&lt;/h2&gt;

&lt;p&gt;If a model scores 95% on a contaminated benchmark, it doesn't necessarily mean it will perform that well on real-world tasks. The model might only be good at repeating what it has seen, not generalizing to new, unseen problems.&lt;/p&gt;

&lt;p&gt;That’s like hiring someone based on their exam score, only to find out they can't solve any new problems—just the ones from past papers.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 So Why Do Model Providers Still Train on Benchmarks?
&lt;/h2&gt;

&lt;p&gt;Great question! Here's why it's not always wrong—and can even be strategic and beneficial:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Improving Real Performance : Some benchmarks are built from high-quality, real-world problems. Including them in training can genuinely help the model become more useful in actual applications. It’s like giving a student the best practice problems—not to cheat, but to prepare them better.&lt;/li&gt;
&lt;li&gt;The Model Will Face Similar Tasks: If users will likely ask questions similar to benchmark data, it makes sense to prepare the model with those examples, ensuring better user experience.&lt;/li&gt;
&lt;li&gt;Everyone Does It (Inadvertently): Most modern models are trained on huge datasets scraped from the internet. If benchmark data was online (papers, datasets, blog posts), it may get included accidentally. This isn’t malicious—it’s just hard to control.&lt;/li&gt;
&lt;li&gt;Strategic Final Training: Many developers do intentional “final tuning” on benchmarks right before release. It's a bit like a student cramming before an exam—not ideal for evaluation, but great for last-mile polish before the model is put into the real world.&lt;/li&gt;
&lt;li&gt;Users Don’t Evaluate Models—They Use Them: Ultimately, users care about how well a model works, not whether it was trained “purely.” If training on benchmarks makes the model more helpful, safer, or smarter, that’s a net positive for most practical use cases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📃Data Contamination Detection Techniques
&lt;/h2&gt;

&lt;p&gt;Proving that a model has seen test data during training isn’t always straightforward—especially when the overlap isn’t exact. Researchers have developed a variety of techniques to detect possible contamination, ranging from direct data matching to more nuanced behavioral analysis. Among these, N-gram Overlap and Perplexity Analysis stand out as two of the most insightful and accessible methods. They help reveal whether a model’s performance is based on true generalization—or subtle memorization of familiar patterns. Let’s take a closer look at how these techniques work, along with examples to make them easier to understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. N-gram Overlap
&lt;/h3&gt;

&lt;p&gt;An n-gram is a short sequence of n consecutive words. For instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The sentence: "Artificial intelligence is transforming industries"&lt;/li&gt;
&lt;li&gt;2-grams: “Artificial intelligence,” “intelligence is,” “is transforming,” “transforming industries”&lt;/li&gt;
&lt;li&gt;3-grams: “Artificial intelligence is,” “intelligence is transforming,” “is transforming industries”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To check for contamination, researchers compare the n-grams in benchmark datasets (used for evaluation) against those in the model’s training data. If many of the same word sequences appear, even without matching the full sentence, that suggests the model may have learned to recognize and rely on familiar phrasing — rather than understanding the meaning from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Perplexity Analysis
&lt;/h3&gt;

&lt;p&gt;Perplexity is a measure of how surprised a language model is when it sees a piece of text. More technically, it reflects how confidently the model can predict each next word in a sentence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low perplexity = the model finds the text very predictable → likely it's seen it (or something very similar) before&lt;/li&gt;
&lt;li&gt;High perplexity = the model is uncertain → it’s encountering unfamiliar or novel phrasing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose a model reads: “Photosynthesis is the process by which plants convert light into energy.”&lt;br&gt;
If the model assigns very low perplexity to this sentence, that likely means it has seen this exact phrasing, or very close variations, during training.&lt;/p&gt;

&lt;p&gt;Now, if this sentence comes from a test benchmark, that low perplexity could be a signal of contamination. The model didn’t have to reason about the answer it just recognized a familiar sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ The Balanced Takeaway
&lt;/h2&gt;

&lt;p&gt;Training on exam questions (i.e., benchmarks) can be misleading if used to brag about scores—but perfectly valid if the goal is to make the model better for actual tasks.&lt;/p&gt;

&lt;p&gt;So it’s not inherently wrong—what matters is transparency. If a model is trained on test data, developers should disclose it so evaluations can be interpreted honestly.&lt;/p&gt;

&lt;p&gt;🌐 For more tech insights, you can find me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Choose the Right AI Model for Your Use Case (Without Going Crazy)</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Tue, 17 Jun 2025 17:10:50 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/how-to-choose-the-right-ai-model-for-your-use-case-without-going-crazy-1ko0</link>
      <guid>https://forem.com/mhamadelitawi/how-to-choose-the-right-ai-model-for-your-use-case-without-going-crazy-1ko0</guid>
      <description>&lt;p&gt;You're building with AI — maybe a chatbot, an agent, a writing assistant, or something more experimental. The code is coming together, the idea is taking shape… and then the real question hits:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Which model should I actually use?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suddenly, you’re lost in a jungle of names: GPT-4, GROK, Mistral, Claude, LLaMA, Gemma... Some are open source. Some are locked behind APIs. Some are fast, others smart, all of them marketed like they’re magic.&lt;/p&gt;

&lt;p&gt;And every source seems to offer conflicting advice. The truth is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s not about picking the  best model in the world — it’s about picking the best model for your job.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post is a practical, developer-focused approach to making smart model choices — without the confusion, wasted resources, or marketing noise. It’s inspired by Chip Huyen’s book, AI Engineering: &lt;em&gt;Building Applications with Foundation Models&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯Start With What You Need
&lt;/h2&gt;

&lt;p&gt;Before diving into model comparisons, define what success looks like for your application. Not hype-worthy demos. What matters is what works for your users — and your goals&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What kind of results do I need? (Accuracy, creativity, safety, etc.)&lt;/li&gt;
&lt;li&gt;What are my non-negotiables? (Privacy, low latency, low cost?)&lt;/li&gt;
&lt;li&gt;What kind of hardware or budget do I have?&lt;/li&gt;
&lt;li&gt;Do I want to use an API or run the model myself?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might seem obvious, but skipping this step is why so many teams waste time testing the wrong models.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Model Selection Is Not One-and-Done
&lt;/h2&gt;

&lt;p&gt;Picking a model isn’t a one-time thing. You’ll probably test and switch models multiple times as your app grows.&lt;/p&gt;

&lt;p&gt;For example , You might start testing with a big fancy model to see if your idea even works. Then try smaller, cheaper models to save cost. Maybe later, you want to finetune a model for better results.&lt;/p&gt;

&lt;p&gt;You’ll keep coming back to this decision—so don’t stress about getting it “perfect” the first time.&lt;/p&gt;

&lt;p&gt;Here’s the core process most teams follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the best achievable performance&lt;/li&gt;
&lt;li&gt;Map models along cost–performance trade-offs&lt;/li&gt;
&lt;li&gt;Choose the best model for your needs and budget&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  💡 Hard vs. Soft Requirements
&lt;/h2&gt;

&lt;p&gt;Think of model features in two buckets:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hard stuff (can’t change easily)&lt;/th&gt;
&lt;th&gt;Soft stuff (can improve or tweak)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model license, training data, size&lt;/td&gt;
&lt;td&gt;Accuracy, speed, safety&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API vs. self-hosted&lt;/td&gt;
&lt;td&gt;Factual quality, response tone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where data is processed (local or cloud)&lt;/td&gt;
&lt;td&gt;Toxicity, helpfulness&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example:&lt;br&gt;
Latency is a soft issue if you host the model and can optimize it. But it’s hard if the model is on someone else’s API and you have no control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build or Buy? Use APIs or Run Your Own Model?
&lt;/h2&gt;

&lt;p&gt;Here’s the classic question:&lt;br&gt;
Should I use a commercial model through an API, or host an open-source model myself?&lt;/p&gt;

&lt;p&gt;There’s no one right answer—it depends on what matters most to you.&lt;br&gt;
✅ Using Commercial APIs (like OpenAI, Anthropic, etc.)&lt;br&gt;
Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to get started&lt;/li&gt;
&lt;li&gt;No server headaches&lt;/li&gt;
&lt;li&gt;Great performance, usually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don’t control the model&lt;/li&gt;
&lt;li&gt;Can’t tweak everything&lt;/li&gt;
&lt;li&gt;Expensive at scale&lt;/li&gt;
&lt;li&gt;Privacy/legal concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Hosting Open Source Models&lt;br&gt;
Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full control&lt;/li&gt;
&lt;li&gt;Better privacy (data stays with you)&lt;/li&gt;
&lt;li&gt;You can finetune or modify as needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Harder to set up&lt;/li&gt;
&lt;li&gt;You need infra, GPUs, and time&lt;/li&gt;
&lt;li&gt;May not match top commercial models in raw power&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🧠 Ask Yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How sensitive is your data?&lt;/li&gt;
&lt;li&gt;Do you need full control or flexibility?&lt;/li&gt;
&lt;li&gt;What’s your team’s technical skill level?&lt;/li&gt;
&lt;li&gt;How fast do you need to scale?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Licensing: The Fine Print That Can Mess You Up
&lt;/h2&gt;

&lt;p&gt;Not all “open-source” models are created equal. Some only share their weights (how the model behaves), but not the training data (what it learned from).&lt;/p&gt;

&lt;p&gt;Before using a model, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can I use this model for commercial stuff?&lt;/li&gt;
&lt;li&gt;Can I use its output to train other models?&lt;/li&gt;
&lt;li&gt;Are there limits on user count or distribution?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read the license (or ask your lawyer). Some models seem open, but have tricky clauses. Better safe than sorry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarks and Leaderboards: Helpful Guides, Not Final Answers
&lt;/h2&gt;

&lt;p&gt;You’ll see lots of leaderboards and benchmarks (like MMLU, TruthfulQA, GSM8K). These test models on different tasks—math, reasoning, trivia, etc.&lt;/p&gt;

&lt;p&gt;These are useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spotting obviously bad models&lt;/li&gt;
&lt;li&gt;Tracking model progress over time&lt;/li&gt;
&lt;li&gt;Getting a rough sense of model strengths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the thing:&lt;br&gt;
Leaderboards are helpful to narrow down options, not to pick your final model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problems with Benchmarks:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Data contamination, models might memorize test data (&lt;a href="https://dev.to/mhamadelitawi/the-leaderboard-illusion-is-your-model-smart-or-just-well-studied-1ndo"&gt;link&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Benchmarks don’t cover all use cases&lt;/li&gt;
&lt;li&gt;A high score doesn’t mean the model will work well for you&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine you’re building a chatbot. A model that does well on math quizzes might still give awful answers to your customers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Your Own Evaluation Tests
&lt;/h3&gt;

&lt;p&gt;Once you’ve picked a few promising models, the best thing to do is run your own tests, using your own data.&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick real tasks your model needs to handle.&lt;/li&gt;
&lt;li&gt;Write test prompts (e.g., customer questions, documents to summarize).&lt;/li&gt;
&lt;li&gt;Define what good looks like (Accuracy? Speed? Tone?)&lt;/li&gt;
&lt;li&gt;Compare models side-by-side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t rely only on numbers—look at outputs with your own eyes. Real-world behavior matters more than benchmark charts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beware of Hidden Costs and Tradeoffs
&lt;/h3&gt;

&lt;p&gt;Let’s break it down:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Commercial APIs&lt;/th&gt;
&lt;th&gt;Open Source (Self-hosted)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔐 Data privacy&lt;/td&gt;
&lt;td&gt;Risky (your data leaves your system)&lt;/td&gt;
&lt;td&gt;Safe (you control everything)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💪 Performance&lt;/td&gt;
&lt;td&gt;Top models available&lt;/td&gt;
&lt;td&gt;Slightly behind but improving&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💻 Setup effort&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Medium to high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💸 Cost&lt;/td&gt;
&lt;td&gt;Pay per use (can get expensive fast)&lt;/td&gt;
&lt;td&gt;Higher setup, lower variable cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎯 Customization&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Full control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧠 Transparency&lt;/td&gt;
&lt;td&gt;Black box&lt;/td&gt;
&lt;td&gt;You can inspect everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🛰️ On device deployment&lt;/td&gt;
&lt;td&gt;Nope&lt;/td&gt;
&lt;td&gt;Possible (if small enough)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Choose based on what’s most important to you. Some teams start with APIs, then switch to self-hosting later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch Out for Model Changes
&lt;/h2&gt;

&lt;p&gt;When you use commercial APIs, the model can change without warning.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
OpenAI might update GPT-4, and suddenly your prompt stops working the same way. It’s happened before. If stability matters to you, this can be a problem.&lt;/p&gt;

&lt;p&gt;With open-source models, you can “freeze” the version and always get the same result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts: The Best Model Is the One That Works for You
&lt;/h3&gt;

&lt;p&gt;Model selection is not a one-time decision—it’s a continuous process of experimentation, evaluation, and iteration. While leaderboards, benchmarks, and market buzz can guide you, the right model is the one that delivers value for your use case under your constraints.&lt;/p&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick models based on your actual needs.&lt;/li&gt;
&lt;li&gt;Run your own evaluations.&lt;/li&gt;
&lt;li&gt;Be ready to switch when things change.&lt;/li&gt;
&lt;li&gt;Keep privacy, cost, and control in mind.&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[0. Start]
  ↓
[1. Filter models by hard requirements]
  ↓
[2. Compare public benchmark data]
  ↓
[3. Run your own evaluation tests]
  ↓
[4. Monitor in production &amp;amp; iterate]
  ↓
[5.Retry if needed]

 🌐 For more tech insights, you can find me on [LinkedIn](https://www.linkedin.com/in/mhamadelitawi).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Payment Gateway Integrations tips</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Wed, 02 Apr 2025 14:34:16 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/payment-gateway-integrations-tips-45no</link>
      <guid>https://forem.com/mhamadelitawi/payment-gateway-integrations-tips-45no</guid>
      <description>&lt;p&gt;Over the past two years, I’ve worked extensively on integrating various payment gateways from different countries. Each integration came with its own challenges, and I’ve gathered key lessons that might help others working on similar projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Documentation Can Be Wrong or Outdated
&lt;/h3&gt;

&lt;p&gt;Many payment gateways provide documentation, but I’ve learned that it’s not always accurate or up to date. Always verify the information through testing and, when in doubt, reach out to support.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Draw the Flow Before Coding
&lt;/h3&gt;

&lt;p&gt;Jumping straight into coding can lead to unnecessary back-and-forth. Instead, take the time to design the integration flow first. This helps in understanding edge cases and ensures a smoother development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Check the Sandbox Account Before Starting
&lt;/h3&gt;

&lt;p&gt;Not all sandbox environments are ready to use immediately. Some require approval or additional configurations. Make sure to check before you begin coding to avoid delays.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Be Cautious with Google Translate
&lt;/h3&gt;

&lt;p&gt;When working with non-English documentation, using a translation tool like Google Translate is helpful. However, be careful—it might change variable names or other critical values, leading to unexpected errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Don’t Limit Order Creation to Payment Success
&lt;/h3&gt;

&lt;p&gt;Ensure orders are created even if payment is pending or fails. This allows for proper tracking, customer notifications, and potential retry attempts.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Pay Attention to 3D Secure
&lt;/h3&gt;

&lt;p&gt;Some gateways have 3D Secure enabled by default, while others require explicit configuration. Ensure you understand how it works for each provider to avoid unexpected declines or authentication issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Log All Requests and Responses
&lt;/h3&gt;

&lt;p&gt;Always log all interactions with the payment gateway, including both successful and failed requests. These logs are invaluable when troubleshooting errors or disputes.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Handle Async Integrations with Care
&lt;/h3&gt;

&lt;p&gt;For integrations that rely on asynchronous processes like webhooks, be aware that failures might not always be communicated properly. Assume that some notifications may be lost and design retry mechanisms where possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Log All Webhooks
&lt;/h3&gt;

&lt;p&gt;Since webhooks are a crucial part of many payment integrations, logging every incoming webhook helps in debugging missing or incorrect transactions. This ensures that no critical information is lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Understand Local Regulations
&lt;/h3&gt;

&lt;p&gt;Some countries have specific regulations around payment processing, such as PCI DSS compliance, data localization laws, or strong customer authentication (SCA). Be aware of these when integrating with gateways in different regions.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Test with Multiple Payment Methods
&lt;/h3&gt;

&lt;p&gt;Many gateways support different payment methods (credit/debit cards, wallets, bank transfers, etc.). Ensure you test all applicable methods, as some might have different behaviors or require extra steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  12. Validate Currency and Conversion Handling
&lt;/h3&gt;

&lt;p&gt;If the gateway supports multiple currencies, check how conversions and fees are handled. Some gateways automatically convert amounts, while others require explicit handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Retry Logic for Transient Failures
&lt;/h3&gt;

&lt;p&gt;Network failures, temporary gateway issues, or timeouts can occur. Implement a robust retry mechanism with exponential backoff for recoverable errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  14. Monitor for Fraud Prevention Mechanisms
&lt;/h3&gt;

&lt;p&gt;Some payment gateways have built-in fraud detection, while others require external tools. Be sure you understand how risk scoring, velocity checks, and blacklisting work.&lt;/p&gt;

&lt;h3&gt;
  
  
  15. Ensure Idempotency for API Requests
&lt;/h3&gt;

&lt;p&gt;For operations like capturing a payment or issuing a refund, ensure requests are idempotent (i.e., repeated requests do not result in duplicate transactions). Some gateways provide idempotency keys to help with this.&lt;/p&gt;

&lt;h3&gt;
  
  
  16. Handle Webhook Security Properly
&lt;/h3&gt;

&lt;p&gt;Always verify the authenticity of webhooks using signature verification or other security measures. This prevents attackers from sending fake webhook requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  17. Build a Robust Error Handling Strategy
&lt;/h3&gt;

&lt;p&gt;Clearly define how to handle different error codes, declined transactions, and exceptions. Consider providing detailed failure messages for users to improve their experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  18. Monitor and Set Up Alerts for Payment Failures
&lt;/h3&gt;

&lt;p&gt;Use logging and monitoring tools to track payment failures in real-time. Setting up alerts for unusual failure rates can help detect potential issues early.&lt;/p&gt;

&lt;h3&gt;
  
  
  19. Plan for Scaling and High Volume Transactions
&lt;/h3&gt;

&lt;p&gt;If you're dealing with high transaction volumes, ensure that your integration can handle concurrency, rate limits, and load spikes efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  20. Read User and Developer Community Feedback
&lt;/h3&gt;

&lt;p&gt;Sometimes, real-world issues are discussed in developer forums, GitHub issues, or Stack Overflow. Checking these sources can help you avoid common pitfalls.&lt;/p&gt;

&lt;h3&gt;
  
  
  21. Consider User Experience (UX) in Payment Flow
&lt;/h3&gt;

&lt;p&gt;A smooth and intuitive checkout process can reduce cart abandonment. Ensure the user experience is seamless across different devices and browsers.&lt;/p&gt;

&lt;h3&gt;
  
  
  22. Be Aware of Gateway-Specific Rate Limits
&lt;/h3&gt;

&lt;p&gt;Many payment gateways impose rate limits on API requests. Exceeding these limits can result in blocked transactions. Implement rate-limiting strategies or queue requests efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  23. Plan for Subscription and Recurring Payments
&lt;/h3&gt;

&lt;p&gt;If your platform supports subscriptions, ensure proper handling of recurring transactions, failed payments, and customer notifications for renewals or card expirations.&lt;/p&gt;

&lt;h3&gt;
  
  
  24. Keep an Eye on Settlement and Reconciliation
&lt;/h3&gt;

&lt;p&gt;Some payment gateways process transactions instantly, while others take days to settle. Implement automated reconciliation to match incoming payments with expected transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  25. Support Multiple Gateways for Redundancy
&lt;/h3&gt;

&lt;p&gt;Relying on a single payment provider can be risky. Consider integrating multiple gateways to ensure uptime and avoid disruptions if one provider experiences downtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  26. Maintain a Documented Flow
&lt;/h3&gt;

&lt;p&gt;Keeping a detailed document about the integration flow helps in debugging, future maintenance, and onboarding new team members. A well-documented process can save hours of troubleshooting.&lt;/p&gt;

&lt;p&gt;By following these practices, I’ve built more reliable payment integrations. If you’re working on a similar project, I hope these insights help!&lt;/p&gt;

&lt;p&gt;🌐 For more tech insights, you can find me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>fintech</category>
      <category>productivity</category>
      <category>api</category>
    </item>
    <item>
      <title>Shutdown EC2 servers when unused</title>
      <dc:creator>Mhamad El Itawi</dc:creator>
      <pubDate>Sat, 08 Feb 2025 13:05:17 +0000</pubDate>
      <link>https://forem.com/mhamadelitawi/shutdown-ec2-servers-when-unused-koa</link>
      <guid>https://forem.com/mhamadelitawi/shutdown-ec2-servers-when-unused-koa</guid>
      <description>&lt;p&gt;In the company where I was working, the process of development passes by 4 different environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local Environment: it’s where the developers usually develop their applications&lt;/li&gt;
&lt;li&gt;Dev Environment: an environment for upcoming or under development features. where all features are deployed and tested together&lt;/li&gt;
&lt;li&gt;Test Environment: an environment used by non developers, mainly sales teams and POs&lt;/li&gt;
&lt;li&gt;Production Environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process introduced the need to deploy the solution on multiple EC2 which unfortunately increased the development cost. Specially that we are using powerful EC2. &lt;/p&gt;

&lt;p&gt;In order to reduce costs, the idea was simple : &lt;strong&gt;Shutdown the server when unused&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The first idea was to make servers available in a specific timeframe, like making them available between 9:00 AM and 6:00 PM. But personally I still believed there was a room for optimization. For example : why do the servers need to be available if the tester is on vacation? what if the person has other tasks to handle? &lt;/p&gt;

&lt;h2&gt;
  
  
  Proposed Architecture
&lt;/h2&gt;

&lt;p&gt;My proposition was to have 2 simples APIs, one for server shutdown and one for startup. These APIs are triggered from a web interface. The shutdown’s one is also triggered by a cron job just in case the employee forgot it when he finishes. Finally, the server is configured with a startup script that runs on restart with root privilege.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgvs10swqc63x5y207x6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgvs10swqc63x5y207x6.png" alt="The proposed architecture" width="606" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The process of implementation will be like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating an IAM policy and Role&lt;/li&gt;
&lt;li&gt;Creating and configuring lambdas&lt;/li&gt;
&lt;li&gt;Configuring the event bridge&lt;/li&gt;
&lt;li&gt;Creating lambda’s client&lt;/li&gt;
&lt;li&gt;Configuring the cloud watch log&lt;/li&gt;
&lt;li&gt;Configuring the startup script&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating IAM policy and Role
&lt;/h3&gt;

&lt;p&gt;Choose role from IAM interface and add the following. Replace accountID  and instanceId by your own.&lt;/p&gt;

&lt;p&gt;The following policy gives the permission to start and stop specific instances, in addition to the ability to write logs to CloudWatch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": "arn:aws:ec2:*:accountID:instance/i-instanceID"
        }
    ]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that go to Role, select AWS service as a trusted entity and lambda as use case, choose the policy created and create the role&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Lambda functions
&lt;/h3&gt;

&lt;p&gt;As mentioned before 2 lambdas functions will be created. one for shutown and one for startup.&lt;/p&gt;

&lt;p&gt;Let’s start by the shutdown.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Lambda function&lt;/li&gt;
&lt;li&gt;Choose Author from scratch&lt;/li&gt;
&lt;li&gt;Set runtime Node (the following code is tested on 12.X)&lt;/li&gt;
&lt;li&gt;Assign the role you created previously&lt;/li&gt;
&lt;li&gt;From advanced options choose enable function URL. This new feature will allow you to proceed without the need of an API gateway&lt;/li&gt;
&lt;li&gt;Set Auth type to NONE (internal app with non critical servers)&lt;/li&gt;
&lt;li&gt;Enable CORS&lt;/li&gt;
&lt;li&gt;Set the following as a lambda code and deploy it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const AWS = require('aws-sdk');
exports.handler = (event, context, callback) =&amp;gt; {
    var instanceId = null;
    var instanceRegion = null;
    if(event.instanceRegion != null &amp;amp;&amp;amp; event.instanceId != null){
        instanceId = event.instanceId;
        instanceRegion = event.instanceRegion;
    } else {
        var obj = JSON.parse(event.body);
        instanceId = obj.instanceId;
        instanceRegion = obj.instanceRegion;
    }
    const ec2 = new AWS.EC2({ region: instanceRegion });
    ec2.stopInstances({ InstanceIds: [instanceId] }).promise()
        .then(() =&amp;gt; callback(null, `Successfully stopped ${instanceId}`))
        .catch(err =&amp;gt; callback(err));
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the startup lambda follow the same steps but add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const AWS = require('aws-sdk');
exports.handler = (event, context, callback) =&amp;gt; {
    var instanceId = null;
    var instanceRegion = null;
    if(event.instanceRegion != null &amp;amp;&amp;amp; event.instanceId != null) {
        instanceId = event.instanceId;
        instanceRegion = event.instanceRegion;
    } else {
        var obj = JSON.parse(event.body);
        instanceId = obj.instanceId;
        instanceRegion = obj.instanceRegion;
    }
    const ec2 = new AWS.EC2({ region: instanceRegion });
    ec2.startInstances({ InstanceIds: [instanceId] }).promise()
        .then(() =&amp;gt; callback(null, `Successfully started ${instanceId}`))
        .catch(err =&amp;gt; callback(err));
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test it following is the body of the request (replace instanceRegion and instanceID by yours):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "instanceRegion": "instanceRegion",
    "instanceId":   "i-instanceID"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating Lambda's client
&lt;/h3&gt;

&lt;p&gt;In order to invoke lambda you need a client. There are many ways to do it, but in order to keep it simple we will use a simple HTML/JS code. It’s a simple POST request. Feel free to use my boilerplate code from my github. The solution can be hosted on S3. &lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Event bridge
&lt;/h3&gt;

&lt;p&gt;This step is a plan B in case the employee forgot to shutdown the server after testing.&lt;/p&gt;

&lt;p&gt;Create a new rule and set data as following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frepw4vky7r3qcsyqk337.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frepw4vky7r3qcsyqk337.jpg" alt="Event bridge configuration" width="768" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the cron pattern:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9maghxaqz8iqieg9zyma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9maghxaqz8iqieg9zyma.png" alt="Cron rule" width="768" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring cloudwatch logs
&lt;/h3&gt;

&lt;p&gt;When lambda’s functions are invoked, they will generate logs. Just make sure to set retention time to the values that suit your business. In order to do it: cloudWatch &amp;gt; Logs &amp;gt; your log path &amp;gt; Retention&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the startup script
&lt;/h3&gt;

&lt;p&gt;You may need to run some scripts on startup in order to boot some services. &lt;/p&gt;

&lt;p&gt;AWS provides something called EC2 user-data. By default this script runs on the creation of the machine with root privilege. It can be modified to make it run on restart. If you want to do it refer to the &lt;a href="https://repost.aws/knowledge-center/execute-user-data-ec2" rel="noopener noreferrer"&gt;AWS article&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Personally, I used a feature on the ubuntu server, and created a service.&lt;/p&gt;

&lt;p&gt;First, create the service file as in the template below in &lt;code&gt;/etc/systemd/system&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/systemd/system/servicefile.service

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the Template as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description = ~Name of the service~

[Service]
WorkingDirectory= ~directory of working file~
ExecStart= ~directory~/filename.sh

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the service by&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl start servicefile.service

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable on startup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl enable servicefile.service

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cost Assessment
&lt;/h2&gt;

&lt;p&gt;The beauty here is that all the components used are serverless. The cost of this integration won’t surpass 1$. A very small price to pay when comparing it to the bill of the unused servers.&lt;/p&gt;

&lt;p&gt;🌐 For more tech insights, you can find me on &lt;a href="https://www.linkedin.com/in/mhamadelitawi" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>webdev</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
