<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Tech-Journey]]></title><description><![CDATA[Hands-on exploration of Linux, backend systems, system design, and DevOps with a focus on building transferable, production-ready engineering skills through rea]]></description><link>https://blog.tech-journey.co.za</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1755777052912/9fd0df23-9b76-43eb-ae26-1c791c59c7e8.png</url><title>Tech-Journey</title><link>https://blog.tech-journey.co.za</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 08:53:15 GMT</lastBuildDate><atom:link href="https://blog.tech-journey.co.za/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[AI is a Tool, You Are the Owner]]></title><description><![CDATA[This guide was initially generated using AI. Rather than writing every sentence from scratch, I used AI as a research, drafting and teaching assistant to help produce a comprehensive first draft.
The ]]></description><link>https://blog.tech-journey.co.za/ai-is-a-tool-you-are-the-owner</link><guid isPermaLink="true">https://blog.tech-journey.co.za/ai-is-a-tool-you-are-the-owner</guid><category><![CDATA[AI]]></category><category><![CDATA[software development]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sat, 06 Jun 2026 08:54:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/3d7b6530-bd51-432b-8e70-dee7f44e5d1c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This guide was initially generated using AI. Rather than writing every sentence from scratch, I used AI as a research, drafting and teaching assistant to help produce a comprehensive first draft.</p>
<p>The prompt instructed the AI to act as an experienced Staff Software Engineer, Engineering Manager and Site Reliability Engineer, and to create a detailed learning guide centred on the following principle:</p>
<blockquote>
<p><em><mark class="bg-yellow-200 dark:bg-yellow-500/30">"AI is a tool, not the owner. The human engineer owns the decision, the implementation and the outcome."</mark></em></p>
</blockquote>
<p>Using AI to generate content doesn't transfer <em>ownership</em> of that content. The ideas presented here were <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">prompted</mark></strong>, <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">reviewed</mark></strong> and <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">intentionally selected</mark></strong> before publication.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">I</mark></strong> have <mark class="bg-yellow-200 dark:bg-yellow-500/30">evaluated</mark> the explanations, <mark class="bg-yellow-200 dark:bg-yellow-500/30">verified</mark> them for accuracy where appropriate, and <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">accepted responsibility</mark></strong> for the final version of this guide.</p>
<p><strong>That distinction is the very subject of this article.</strong></p>
<p>Just as an engineer remains accountable for code that was written with the assistance of AI, I remain accountable for the content I share🙂. AI accelerated the drafting process, but it didn't decide to publish this article, nor does it assume responsibility for any inaccuracies or omissions. Those decisions remain <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">mine</mark></strong>.</p>
<p>The goal here is to explain <em><strong>why</strong></em> AI should be viewed as a powerful engineering tool rather than the owner of technical decisions. Whether you are writing code, designing cloud infrastructure, reviewing security controls or documenting operational processes, the responsibility for understanding, validating and standing behind the final outcome continues to rest with the human engineer.</p>
<p>With that context established, let's explore what ownership looks like in the age of AI.</p>
<hr />
<h2>The Core Principle: AI is a Tool, You Are the Owner</h2>
<p>In modern software engineering, AI is a powerful assistant, but its not the owner of the systems we build. When a system succeeds, its because human engineers designed, verified, and deployed it correctly (correctly in this context doesn't necessarily mean perfect, secure and/or scalable). When a system fails, <mark class="bg-yellow-200 dark:bg-yellow-500/30">the responsibility lies with the humans who approved the changes.</mark></p>
<p>Organisations assign responsibility to <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">people</mark></strong> rather than AI systems because AI lacks legal standing, ethical judgement, and operational awareness. <strong>Accountability</strong> means taking ownership of the consequences of an action. An AI <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">cannot</mark></strong> be <strong>fired</strong>, <strong>sued</strong>, or <strong>put on an on-call rotation</strong> to fix a broken prod environment over a weekend or at "2AM".</p>
<p>Using AI as an assistant means leveraging it to generate templates, suggest solutions, or automate "boilerplate" code. Delegating ownership, which is a critical mistake, means accepting these outputs without verification. "AI built it" <strong>undermines your value</strong> as an engineer. "AI broke it" is an <strong>unacceptable excuse</strong> for failing to review what you deployed.</p>
<hr />
<h2>Ownership Throughout the Software Development Lifecycle (SDLC)</h2>
<p><em>The SDLC is the process used to design, develop, and test high-quality software</em>. AI can accelerate this process, but the engineer must maintain control at every step.</p>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">1. Requirements Gathering</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Summarising meeting notes, drafting user stories, or suggesting edge cases based on prompts.</p>
</li>
<li><p><strong>Engineer must own:</strong> Validating that the requirements solve the actual business problem and aligning them with stakeholders.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Building a perfectly functioning feature that nobody needs because the AI misunderstood the business context.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">2. System Design</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Proposing architectural patterns or comparing database technologies.</p>
</li>
<li><p><strong>Engineer must own:</strong> Choosing the final architecture based on budget, team skills, and non-functional requirements (like scalability and compliance).</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Adopting an overly complex, distributed microservices architecture for a simple internal tool.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">3. Threat Modelling (Identifying Security Risks)</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Generating a baseline list of common threats for a given architecture.</p>
</li>
<li><p><strong>Engineer must own:</strong> Contextualising threats. Only the engineer knows the true value of the data being protected.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Missing bespoke logic flaws that generic AI models do not recognise.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">4. Development</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Generating boilerplate code, writing functions, or explaining syntax.</p>
</li>
<li><p><strong>Engineer must own:</strong> Ensuring the code is maintainable, follows team standards (like SOLID principles), and accurately implements the requirements.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Introducing subtle bugs or technical debt by pasting code you do not fully understand.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">5. Code Review</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Spotting syntax errors or highlighting missing unit tests.</p>
</li>
<li><p><strong>Engineer must own:</strong> Evaluating the logic, architectural fit, and clarity of the code.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Approving a pull request that looks syntactically correct but breaks existing business logic.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">6. Security Review</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Flagging known vulnerable patterns or outdated dependencies.</p>
</li>
<li><p><strong>Engineer must own:</strong> Verifying the findings, investigating false positives, and ensuring secure logic (e.g., proper access controls).</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Trusting the AI's "clean bill of health" while a severe authorisation flaw remains hidden in the logic.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">7. Testing</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Generating mock data or drafting standard unit tests.</p>
</li>
<li><p><strong>Engineer must own:</strong> Defining the test strategy, writing complex integration tests, and ensuring edge cases are covered.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> AI writes tests that merely assert the generated code does what it does, rather than testing what it <em>should</em> do.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">8. CI/CD Pipeline</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Writing workflow scripts (e.g., GitHub Actions YAML).</p>
</li>
<li><p><strong>Engineer must own:</strong> Securing the pipeline, managing secrets, and ensuring deployment gates are enforced.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> A generated pipeline script exposes an environment variable containing a database password in the build logs.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">9. Production Deployment</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Drafting release notes or rollout plans.</p>
</li>
<li><p><strong>Engineer must own:</strong> The decision to press "deploy" and the readiness to roll back if things fail.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Deploying during peak business hours because the AI-generated schedule lacked timezone awareness.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">10. Monitoring</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Writing query syntax to create dashboards or suggesting alert thresholds.</p>
</li>
<li><p><strong>Engineer must own:</strong> Defining the Service Level Objectives (SLOs) that actually matter to the user experience.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Alert fatigue from overly sensitive, AI-suggested monitors that wake engineers for non-issues.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">11. Incident Response</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Parsing logs quickly or suggesting potential root causes.</p>
</li>
<li><p><strong>Engineer must own:</strong> Making the final call on mitigation strategies and communicating with stakeholders.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Executing a destructive AI-suggested database command while panicking during an outage.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">12. Post-Incident Review</mark></h3>
<ul>
<li><p><strong>AI can assist with:</strong> Summarising chat transcripts into a timeline.</p>
</li>
<li><p><strong>Engineer must own:</strong> Identifying the systemic failures and committing to actionable preventive measures.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What can go wrong:</mark></strong> Producing a generic, shallow report that fails to address the specific cultural or technical root cause.</p>
</li>
</ul>
<hr />
<h2>Realworld Examples</h2>
<p>Here we look at practical scenarios using a modern enterprise stack.</p>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example 1: Creating a CI/CD Pipeline</mark></h3>
<ul>
<li><p><strong>The Task:</strong> Create a GitHub Actions workflow to build a Docker image for a Python Django application and push it to a registry.</p>
</li>
<li><p><strong>What AI suggested:</strong> A valid YAML file that builds the Docker image and uses <code>docker-compose</code> to run tests. However, it hardcoded the registry credentials in the YAML file.</p>
</li>
<li><p><strong>What the engineer reviewed:</strong> The engineer noticed the hardcoded credentials and the lack of a specific base image tag (it used <code>python:latest</code>).</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The final decision:</mark></strong> The engineer modified the YAML to pull credentials securely from GitHub Secrets and pinned the Docker image to a specific version (<code>python:3.11-slim</code>).</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Potential business impact if incorrect:</mark></strong> Hardcoded credentials could be leaked to anyone with repository access, allowing malicious actors to replace production images.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example 2: Building a REST API Endpoint</mark></h3>
<ul>
<li><p><strong>The Task:</strong> Create a Django REST API endpoint that allows a user to fetch their billing history from a PostgreSQL database.</p>
</li>
<li><p><strong>What AI suggested:</strong> A clean, syntactically correct endpoint. However, the query filtered only by <code>invoice_id</code> and did not verify if the requesting user owned that invoice.</p>
</li>
<li><p><strong>What the engineer reviewed:</strong> The engineer recognised a Missing Object-Level Authorisation flaw (also known as Insecure Direct Object Reference or IDOR).</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The final decision:</mark></strong> The engineer rewrote the query to filter by both <code>invoice_id</code> and <a href="http://request.user.id"><code>request.user.id</code></a>.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Potential business impact if incorrect:</mark></strong> Customers could guess other users' invoice IDs and view confidential financial data, leading to a massive privacy breach and legal fines.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example 3: Provisioning Cloud Infrastructure</mark></h3>
<ul>
<li><p><strong>The Task:</strong> Use AWS to host a static frontend securely via CloudFront and route traffic using Route 53, while a Lambda function retrieves an API key from Secrets Manager.</p>
</li>
<li><p><strong>What AI suggested:</strong> Provided the correct Terraform code for CloudFront and Route 53, but the IAM (Identity and Access Management) role assigned to the Lambda function was granted <code>secretsmanager:*</code> (full access to all secrets).</p>
</li>
<li><p><strong>What the engineer reviewed:</strong> The engineer identified that the IAM policy violated the Principle of Least Privilege.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The final decision:</mark></strong> The engineer scoped the IAM policy down to <code>secretsmanager:GetSecretValue</code> for only the specific API key's Resource ARN (Amazon Resource Name).</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Potential business impact if incorrect:</mark></strong> If the Lambda function were compromised, the attacker could read every secret in the AWS account, including database passwords and third-party API keys.</p>
</li>
</ul>
<hr />
<h2>Security Perspective</h2>
<p>AI models prioritise providing a <em><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">functional</mark></strong></em> answer over a secure one (<strong>This has been my experience on numerous occasions especially around docker image builds and third-party libraries</strong>). It often generates code based on historical, sometimes outdated, public repositories.</p>
<table style="min-width:125px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Security Risk</strong></p></td><td><p><strong>How it happens (AI Context)</strong></p></td><td><p><strong>Detection</strong></p></td><td><p><strong>Prevention</strong></p></td><td><p><strong>Tools</strong></p></td></tr><tr><td><p><strong>Security Vulnerabilities</strong></p></td><td><p>AI uses outdated functions (e.g., MD5 for hashing).</p></td><td><p>Code review, static analysis.</p></td><td><p>Enforce modern cryptography standards.</p></td><td><p>SonarQube, Bandit.</p></td></tr><tr><td><p><strong>Logic Errors</strong></p></td><td><p>AI misinterprets complex business rules (e.g., refunding the wrong amount).</p></td><td><p>Unit testing, QA.</p></td><td><p>Write clear tests before using AI.</p></td><td><p>Pytest, JUnit.</p></td></tr><tr><td><p><strong>Race Conditions</strong></p></td><td><p>AI writes asynchronous code without proper locking mechanisms.</p></td><td><p>Load testing, concurrency testing.</p></td><td><p>Deep understanding of threading models.</p></td><td><p>JMeter, Thread Sanitizer.</p></td></tr><tr><td><p><strong>Authentication Mistakes</strong></p></td><td><p>AI suggests custom auth logic instead of standard libraries.</p></td><td><p>Security architecture review.</p></td><td><p>Always use established frameworks (e.g., OAuth).</p></td><td><p>OWASP ZAP, Burp Suite.</p></td></tr><tr><td><p><strong>Authorisation Flaws</strong></p></td><td><p>AI forgets to check if a user <em>owns</em> the resource they are accessing.</p></td><td><p>Manual code review, penetration testing.</p></td><td><p>Implement mandatory access control checks.</p></td><td><p>Semgrep, manual review.</p></td></tr><tr><td><p><strong>Secret Leakage</strong></p></td><td><p>AI placeholders (e.g., <code>api_key = "12345"</code>) are accidentally committed.</p></td><td><p>Pre-commit hooks scanning for secrets.</p></td><td><p>Use environment variables and secret managers.</p></td><td><p>GitLeaks, TruffleHog.</p></td></tr><tr><td><p><strong>Supply Chain Risks</strong></p></td><td><p>AI suggests importing a helpful but unmaintained or malicious package.</p></td><td><p>Dependency auditing.</p></td><td><p>Vet all new third-party libraries.</p></td><td><p>Snyk, Dependabot, socket.dev</p></td></tr><tr><td><p><strong>Dependency Vulnerabilities</strong></p></td><td><p>AI suggests a specific version of a package that has known CVEs.</p></td><td><p>Software Composition Analysis (SCA).</p></td><td><p>Automate dependency checks in CI/CD.</p></td><td><p>OWASP Dependency-Check.</p></td></tr><tr><td><p><strong>Misconfigured Cloud</strong></p></td><td><p>AI generates permissive IAM policies or open security groups (0.0.0.0/0).</p></td><td><p>Infrastructure code scanning.</p></td><td><p>Apply Principle of Least Privilege.</p></td><td><p>Checkov, tfsec.</p></td></tr></tbody></table>

<hr />
<h2>Incident Ownership: Case Studies</h2>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Case Study 1: Infrastructure Code Failure</mark></h3>
<ul>
<li><p><strong>Timeline:</strong> 10:00 AM: Engineer prompts AI for Terraform to add a read-replica to PostgreSQL. 10:05 AM: Engineer applies the code. 10:10 AM: Primary database drops.</p>
</li>
<li><p><strong>Root Cause:</strong> The AI generated a script that recreated the database cluster instead of appending a replica, destroying the primary volume.</p>
</li>
<li><p><strong>Technical Investigation:</strong> The Terraform state showed a <code>destroy</code> and <code>create</code> action, which the engineer ignored during the <code>terraform plan</code> phase.</p>
</li>
<li><p><strong>Impact on Customers:</strong> 45 minutes of total application downtime.</p>
</li>
<li><p><strong>Recovery Process:</strong> Restored database from the most recent snapshot.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Lessons Learned:</mark></strong> Always review the execution plan of infrastructure-as-code before applying.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Accountability:</mark></strong> The engineer is accountable for failing to review the <code>terraform plan</code> output. AI simply provided a string of text.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Case Study 2: Application Code Race Condition</mark></h3>
<ul>
<li><p><strong>Timeline:</strong> Black Friday sale begins. 12:00 PM: Support receives calls about negative inventory.</p>
</li>
<li><p><strong>Root Cause:</strong> An AI-generated Python function for deducting inventory checked stock levels and updated them in two separate database calls without an atomic transaction.</p>
</li>
<li><p><strong>Technical Investigation:</strong> High concurrent traffic allowed multiple users to pass the "check stock" conditional before any of them updated the database, leading to overselling.</p>
</li>
<li><p><strong>Impact on Customers:</strong> Hundreds of orders had to be cancelled and refunded, damaging reputation.</p>
</li>
<li><p><strong>Recovery Process:</strong> Rolled back to previous version; implemented <code>SELECT ... FOR UPDATE</code> row-level locking.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Lessons Learned:</mark></strong> AI cannot predict concurrency issues under load; engineers must apply database locking strategies.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Accountability:</mark></strong> The engineer is accountable for not testing the code under concurrent load or understanding database transaction isolation.</p>
</li>
</ul>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Case Study 3: Security Configuration Breach</mark></h3>
<ul>
<li><p><strong>Timeline:</strong> 02:00 AM: Unauthorised data exfiltration detected from AWS S3.</p>
</li>
<li><p><strong>Root Cause:</strong> An engineer used AI to create an S3 bucket policy. The AI suggested a policy allowing <code>*</code> (public) read access to fix a "permission denied" error the engineer was facing.</p>
</li>
<li><p><strong>Technical Investigation:</strong> The bucket contained PII (Personally Identifiable Information). The public policy was active for 72 hours.</p>
</li>
<li><p><strong>Impact on Customers:</strong> Legal compliance breach requiring customer notification and regulatory fines.</p>
</li>
<li><p><strong>Recovery Process:</strong> S3 public access blocked; forensic audit of accessed files.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Lessons Learned:</mark></strong> Never solve "permission denied" errors by granting public access.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Accountability:</mark></strong> The engineer is accountable for deploying a policy that violated basic security principles to save time.</p>
</li>
</ul>
<hr />
<h2>Why AI Doesn't Remove Engineering Skill</h2>
<p>To effectively verify AI output, an engineer must possess deep fundamental knowledge. If you don't understand the underlying system, you cannot tell when the AI is wrong.</p>
<ul>
<li><p><strong>Operating Systems:</strong> If AI suggests a Bash script to manage file descriptors, you must understand kernel limits and process lifecycles to know if it will crash the server.</p>
</li>
<li><p><strong>Networking:</strong> If AI suggests modifying a Docker bridge network, you must understand subnets and NAT (Network Address Translation) to ensure containers can still reach the internet securely.</p>
</li>
<li><p><strong>Databases:</strong> AI will happily write an SQL query that scans an entire 100-million-row table. You must understand indexing and execution plans to ensure performance.</p>
</li>
<li><p><strong>Security:</strong> AI cannot understand the business context of data. You must know what constitutes sensitive data to protect it adequately.</p>
</li>
<li><p><strong>System Architecture:</strong> AI might suggest caching everything in Redis. You must understand cache invalidation and memory limits to design a robust system.</p>
</li>
</ul>
<hr />
<h2>AI as an Engineering Tool</h2>
<p>AI is a tool, joining a long lineage of developer aids.</p>
<table style="min-width:75px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Tool</strong></p></td><td><p><strong>Strengths</strong></p></td><td><p><strong>Limitations</strong></p></td></tr><tr><td><p><strong>AI Assistants</strong></p></td><td><p>Fast, synthesises context, generates boilerplate.</p></td><td><p>Can hallucinate confidently, lacks business context.</p></td></tr><tr><td><p><strong>Google Search</strong></p></td><td><p>Broad access to diverse, real-world opinions.</p></td><td><p>Requires sifting through irrelevant or outdated SEO spam.</p></td></tr><tr><td><p><strong>Stack Overflow</strong></p></td><td><p>Community-vetted answers with upvotes and discussion.</p></td><td><p>Often highly specific to a slightly different problem than yours.</p></td></tr><tr><td><p><strong>Documentation</strong></p></td><td><p>The ultimate source of truth for a tool.</p></td><td><p>Can be dense, difficult to read, or lack practical examples.</p></td></tr><tr><td><p><strong>IDE Autocomplete</strong></p></td><td><p>Saves keystrokes, prevents typos.</p></td><td><p>Only predicts the next few words; lacks architectural vision.</p></td></tr><tr><td><p><strong>Linters/Compilers</strong></p></td><td><p>Enforces objective syntax and style rules.</p></td><td><p>Cannot catch logical business errors.</p></td></tr><tr><td><p><strong>Senior Colleagues</strong></p></td><td><p>Understands business context, culture, and architecture.</p></td><td><p>Limited time and availability.</p></td></tr></tbody></table>

<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Key difference:</mark></strong> A compiler throws an error if your code is wrong. An AI will confidently explain why its incorrect code is right. <mark class="bg-yellow-200 dark:bg-yellow-500/30">You must treat AI like a junior developer who is eager to please but occasionally invents facts</mark>.</p>
<hr />
<h2>Professional Responsibility</h2>
<p>Engineering organisations use specific gates to enforce human accountability. <strong>None of these can be transferred to an AI.</strong></p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Pull Requests (PRs) &amp; Code Reviews:</mark></strong> The reviewer acts as a human gatekeeper. They sign off on the logic, security, and maintainability of the code.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Change Approval Board (CAB):</mark></strong> A human committee that evaluates the risk of a deployment against business operations.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">On-call Rotations:</mark></strong> Humans are paid to wake up and fix systems because they can think critically under pressure and take legal/financial responsibility for outages.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Blameless Postmortems:</mark></strong> We investigate <em>how</em> a system allowed a failure, rather than punishing the human who made the mistake. However, the human is still responsible for participating, learning, and improving the system. You cannot invite an AI to a postmortem.</p>
</li>
</ul>
<hr />
<h2>Beyond Software Engineering: A Universal Principle</h2>
<p>While this guide has focused primarily on the Software Development Lifecycle, cloud infrastructure, and enterprise architecture, the core principle—<em><mark class="bg-yellow-200 dark:bg-yellow-500/30">AI is a tool</mark>, <mark class="bg-yellow-200 dark:bg-yellow-500/30">not the owner</mark></em>—is entirely universal. The concepts of accountability, verification, and critical analysis apply to almost every modern profession.</p>
<p>Whether you are a medical doctor using AI to assist with diagnostics, a lawyer generating legal research, or a financial analyst modelling market trends, the professional boundaries remain identical:</p>
<ul>
<li><p><strong>In Medicine:</strong> An AI might rapidly flag a potential anomaly on an MRI scan, but the consultant physician must own the final diagnosis and treatment plan. An AI cannot be held liable for a misdiagnosis.</p>
</li>
<li><p><strong>In Law:</strong> An AI might draft a comprehensive contract or search through decades of case law, but if it hallucinates (<mark class="bg-yellow-200 dark:bg-yellow-500/30">invents</mark>) a legal precedent, the human lawyer faces professional sanctions or disbarment, not the software provider.</p>
</li>
<li><p><strong>In Finance:</strong> An AI might analyse global supply chain data to recommend an investment strategy, but the financial advisor and their institution must answer to regulatory bodies and clients if the risk assessment is fundamentally flawed.</p>
</li>
</ul>
<p>Across all disciplines, AI is an extraordinary lever. It scales capability, speed, and pattern recognition to unprecedented levels. However, it cannot scale accountability, liability, or ethical judgement. True professionals in any field differentiate themselves not by how much work they can blindly delegate to a machine, but by the depth of understanding, rigorous validation, and ultimate responsibility they bring to the final outcome.</p>
]]></content:encoded></item><item><title><![CDATA[Local Dev Tools and Security Implications]]></title><description><![CDATA[Modern dev environments rely heavily on third-party extensions. Many of these extensions execute code locally and may have access to source code, config files, credentials, and other sensitive data.
A]]></description><link>https://blog.tech-journey.co.za/how-your-code-editor-can-become-a-security-risk</link><guid isPermaLink="true">https://blog.tech-journey.co.za/how-your-code-editor-can-become-a-security-risk</guid><category><![CDATA[vscode extensions]]></category><category><![CDATA[IDEs]]></category><category><![CDATA[vulnerability]]></category><category><![CDATA[devtools]]></category><category><![CDATA[Security]]></category><category><![CDATA[cybersecurity]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sat, 14 Feb 2026 11:21:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771435374776/9bfd3395-6009-4f46-b2d4-ec51c3f5f134.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Modern dev environments rely heavily on third-party extensions. Many of these extensions execute code locally and may have access to source code, config files, credentials, and other sensitive data.</p>
<p>A recent write-up from OX Security examined vulnerabilities in widely used IDE extensions, showing how flaws in extension design can enable remote code execution and local file exfiltration.</p>
<p>The exposure isn't limited to software engineers. Anyone using an IDE for scripting, automation, infrastructure management, or data processing operates within the same environment and is subject to many of the same risks.</p>
<blockquote>
<p>Reference:<br /><a href="https://www.ox.security/blog/four-vulnerabilities-expose-a-massive-security-blind-spot-in-ide-extensions/?utm_source=chatgpt.com">https://www.ox.security/blog/four-vulnerabilities-expose-a-massive-security-blind-spot-in-ide-extensions/</a></p>
</blockquote>
<hr />
<h2>IDE Extensions and Privileged Access</h2>
<p>VS Code and similar editors are deeply embedded in daily development workflows. That familiarity can make it easy to overlook how much access installed extensions may have.</p>
<p>Depending on their functionality, extensions can interact with:</p>
<ul>
<li><p>Source code and build artifacts</p>
</li>
<li><p>Environment variables and configuration files</p>
</li>
<li><p>Credentials such as API tokens, SSH keys, and cloud access keys</p>
</li>
</ul>
<p>Unlike app dependencies, extensions are often installed to solve an immediate problem and then forgotten. Automatic updates are common, permissions are rarely revisited, and extensions can remain present for years without review.</p>
<p>Many dev environments therefore contain software with access to code, systems, and credentials that receives little ongoing scrutiny. For security teams, this represents a blind spot that is easy to overlook and difficult to quantify.</p>
<hr />
<h2>Findings from the OX Security Research</h2>
<p>The OX Security report identified vulnerabilities in several widely used IDE extensions, including issues that could be abused to execute code or access local files.</p>
<p>Examples included:</p>
<ul>
<li><p>Remote code execution through exposed extension functionality</p>
</li>
<li><p>Local file exfiltration through crafted inputs</p>
</li>
<li><p>Insufficient validation of data received from external sources</p>
</li>
</ul>
<p>The vulnerabilities were identified in extensions that were already in widespread use.</p>
<hr />
<h2>The Visibility Gap</h2>
<p>The challenge often comes down to visibility.</p>
<p>In many environments:</p>
<ul>
<li><p>Extension inventories are not centrally tracked.</p>
</li>
<li><p>Permissions are not reviewed regularly.</p>
</li>
<li><p>Extensions remain installed long after their original purpose has passed.</p>
</li>
<li><p>Formal reassessment and removal processes are uncommon.</p>
</li>
</ul>
<p>As a result, IDE extensions gradually become part of the attack surface. They exist across developer workstations, have access to source code and local resources, and frequently receive little attention during security reviews.</p>
<hr />
<h2>Listing Installed VS Code Extensions on macOS</h2>
<p>If you want to audit extensions, the first step is simply knowing what is installed.</p>
<p>On macOS, you can list all your VS Code extensions by running:</p>
<pre><code class="language-bash">ls ~/.vscode/extensions
</code></pre>
<p>Each directory follows this format:</p>
<pre><code class="language-bash">publisher.extension-version
</code></pre>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Important:</mark> Only include the Extension ID and <mark class="bg-yellow-200 dark:bg-yellow-500/30">NOT</mark> the version number</strong></p>
<p>Extension scanning and auditing tools, such as <a href="https://vscan.dev/">VSCan</a>, expect the extension ID in the following format otherwise the tool will not recognise it:</p>
<pre><code class="language-bash">publisher.extension #nothing else
</code></pre>
<p>To extract clean extension IDs on macOS, run this in your terminal:</p>
<pre><code class="language-bash">for dir in ~/.vscode/extensions/*; do
  name=\((basename "\)dir")
  echo "${name%-*}"
done
</code></pre>
<p>This list can then be used for:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Manual review</mark></strong> – Check if extensions like <code>ms-python.python</code> or <code>eamodio.gitlens</code> are really needed, and <strong>remove</strong> any that are unused.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Feeding into scanning tools</mark></strong> – tools such as <strong>VSCan</strong> can analyse each extension for risky behaviours or suspicious permissions.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Cross-referencing against vulnerability disclosures and advisories</mark></strong> – See if installed extensions match those flagged in vulnerability reports, such as the OX Security blog highlighting <code>Live Server</code> or <code>Markdown Preview Enhanced</code>.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Staying informed</mark></strong> – Devs should consider following sources like <a href="https://thehackernews.com/">The Hacker News</a>, security blogs, and advisory feeds to stay up to date on new extension vulnerabilities and patches.</p>
</li>
</ul>
<p>Here’s an example of the output you can expect to see from VSCan:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771434463907/f7be4ae1-1e04-47c4-9ea3-a916c31d592d.png" alt="" style="display:block;margin:0 auto" />

<blockquote>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Risk indicators are not absolute</mark>: Even well-maintained extensions can introduce exposure depending on how they are configured, what files they can access, and whether they have network reach. The actual risk is determined less by the extension itself and more by the environment it runs in and the privileges it operates under.</p>
</blockquote>
<hr />
<h2>Keep Extension Audits Simple</h2>
<p>Unlike system packages or backend dependencies, IDE extensions don't have a mature, centralised vulnerability tracking ecosystem. That makes conventional dependency scanning only partially effective.</p>
<p>In environments where no formal process exists, tools such as <a href="https://vscan.dev/">VSCan</a> can help by analysing installed extensions for risky behaviour patterns and known security concerns. The goal is to replace assumption-based trust with observable facts about what each extension actually does.</p>
<p>From there, a lightweight operational baseline is often enough:</p>
<ul>
<li><p>Periodic review of installed extensions to ensure each one still serves a purpose</p>
</li>
<li><p>Removal of unused or redundant extensions to reduce overall attack surface</p>
</li>
<li><p>Preference for extensions that are actively maintained or published by trusted sources</p>
</li>
</ul>
<p>This approach avoids unnecessary process overhead while still reducing exposure in a meaningful way.</p>
<hr />
<h2>The IDE is Part of the Supply Chain</h2>
<p>IDE extensions operate within the same trust boundary as the rest of your software stack. They may look like productivity tools, but they still execute code and interact with sensitive data.</p>
<p>Addressing this does not require complex policies or restrictive controls. It starts with visibility and a habit of asking a simple question:</p>
<p><strong>What is running inside this environment?</strong></p>
<p>Whether you're developing applications, processing data, or managing infrastructure, your editor is part of the software supply chain. It should be evaluated with the same level of attention as any other dependency.</p>
]]></content:encoded></item><item><title><![CDATA[Part 1: Ubuntu System Hardening and Execution Baseline]]></title><description><![CDATA[A fresh Ubuntu installation is designed to be permissive, catering equally to desktop, server, and container workloads without assuming a strict security model.
However, the moment a system is exposed]]></description><link>https://blog.tech-journey.co.za/part-1-ubuntu-system-hardening-and-execution-baseline</link><guid isPermaLink="true">https://blog.tech-journey.co.za/part-1-ubuntu-system-hardening-and-execution-baseline</guid><category><![CDATA[Ubuntu]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Fri, 02 Jan 2026 08:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/4a6f2900-c753-424a-bdbe-346974739180.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A fresh Ubuntu installation is designed to be permissive, catering equally to desktop, server, and container workloads without assuming a strict security model.</p>
<p>However, the moment a system is exposed to a network (internal/external), its threat model changes. It transitions from an isolated compute environment into a reachable execution target. What follows is the process of moving from this default, permissive state to a controlled execution environment, ending with an operational monitoring model which will be covered in <strong>Part Two</strong>.</p>
<h2>System Exposure and Baseline Inspection</h2>
<p>Before altering the system, you need to understand its current exposure.<br /><strong>Network listeners:</strong></p>
<pre><code class="language-shell">sudo ss -tulnp
</code></pre>
<p>This command reveals which processes are actively bound to network interfaces. At this stage, the network layer makes no distinction between intended services (like SSH) and accidental exposure (like a default database binding to <code>0.0.0.0</code>).</p>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/d7add587-6e72-4d4b-8c3d-d9e8bfb17176.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Installed package drift:</strong></p>
<pre><code class="language-shell">apt list --upgradable
</code></pre>
<p>This command shows the gap between what’s installed locally and what the repositories currently provide. That gap is where security debt accumulates.</p>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/8362eb02-12e4-45f4-ae17-8aabd680cde7.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Enabled services:</strong></p>
<pre><code class="language-shell">systemctl list-unit-files --state=enabled
</code></pre>
<p>This command lists everything configured to execute automatically during boot. Its the most accurate representation of the system's baseline behaviour.</p>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/128cb36a-899f-4746-88d2-9c9798078afd.png" alt="" />

<hr />
<h3>Package State Alignment</h3>
<p>Systems degrade securely over time if they're not consistently aligned with upstream security patches.</p>
<p><strong>Update alignment:</strong></p>
<pre><code class="language-shell">sudo apt update &amp;&amp; sudo apt upgrade -y
</code></pre>
<p><strong>Verification:</strong></p>
<pre><code class="language-shell">apt list --upgradable
</code></pre>
<p>The expected steady state is an <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">empty</mark></strong> upgrade list. Anything else represents drift that needs to be addressed.</p>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Expected output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/0c251773-4b7f-4fdb-ba45-5f6fae4b704e.png" alt="" />

<hr />
<h3>Automating Security Updates</h3>
<p>Manual update cycles rely on human consistency, which is inherently flawed. Security patch latency (the window between vulnerability disclosure, package availability, and human execution) is where the majority of exploitation occurs.</p>
<p><strong>Enable unattended updates:</strong></p>
<pre><code class="language-shell">sudo apt install unattended-upgrades -y
</code></pre>
<blockquote>
<p><em>Note: you might already have this package installed on your system.</em></p>
</blockquote>
<p><strong>Verify behaviour configuration:</strong></p>
<pre><code class="language-shell">cat /etc/apt/apt.conf.d/20auto-upgrades
</code></pre>
<p>This file dictates whether update scheduling is successfully delegated to the system. You should see <code>APT::Periodic::Update-Package-Lists "1";</code> and <code>APT::Periodic::Unattended-Upgrade "1";</code>.</p>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/b9f5eddd-5869-46e2-81d6-ef6dcc43c37e.png" alt="" />

<hr />
<h3>SSH as an Execution Boundary</h3>
<p>SSH operates as a remote execution boundary at the operating system level. Once access is established, the session effectively represents direct command execution on the host. When password authentication is enabled, that boundary is governed primarily by password strength and any rate-limiting controls enforced by the service.</p>
<p>Removing password authentication reduces the system to key-based identity verification only, but this change must be executed with a verified fallback path already in place.</p>
<blockquote>
<p><strong>Warning:</strong> Before disabling password authentication, ensure you have successfully generated and copied your SSH public key to <code>~/.ssh/authorized_keys</code> on the remote server. Otherwise, you will lock yourself out.</p>
</blockquote>
<h3>Precondition: Verify SSH Key Access</h3>
<p>From your local machine, confirm you can log in using SSH:</p>
<pre><code class="language-shell">ssh user@server_ip
</code></pre>
<p>If a password is still required, key authentication hasn't been configured yet.</p>
<p>You can explicitly test key usage:</p>
<pre><code class="language-shell">ssh -i ~/.ssh/id_rsa user@server_ip
</code></pre>
<p>Ensure your public key exists on the remote server:</p>
<pre><code class="language-shell">cat ~/.ssh/authorized_keys
</code></pre>
<p>If its missing, copy it using:</p>
<pre><code class="language-shell">ssh-copy-id user@server_ip
</code></pre>
<p>Make sure permissions are correct:</p>
<pre><code class="language-shell">chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
</code></pre>
<blockquote>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Keep an active SSH session open while making changes, in case rollback is needed.</mark></p>
</blockquote>
<p><strong>SSH configuration:</strong><br />Modifying the <code>/etc/ssh/sshd_config</code> file.</p>
<pre><code class="language-shell">sudo sed -i 's/^#*PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#*PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
</code></pre>
<p>The commands above will ensure the following directives are set:</p>
<pre><code class="language-plaintext">PasswordAuthentication no
PermitRootLogin no
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/41441b3a-072d-4a63-90db-69b0715185a3.png" alt="" />

<p><strong>Apply and verify the changes:</strong></p>
<pre><code class="language-shell">sudo systemctl restart ssh
sudo sshd -T | grep -i passwordauthentication
</code></pre>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/9b45eff8-97f3-49f3-8e18-89a273488692.png" alt="" />

<p>After this change, SSH access depends entirely on key-based authentication. Password login is no longer accepted by the service configuration.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example of key-based authentication:</mark></strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/d2140bde-56c4-4453-a4b3-6cfbce2dcca3.png" alt="" />

<hr />
<h3>The Firewall as Traffic Definition</h3>
<p>A firewall does not inherently secure a vulnerable application, but it enforces strict network boundaries, defining exactly which paths are valid.</p>
<p><strong>Configure UFW (Uncomplicated Firewall):</strong></p>
<pre><code class="language-shell">sudo ufw allow OpenSSH
sudo ufw enable
</code></pre>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/ea1cfde1-169f-412c-8c31-a653896af182.png" alt="" />

<p>Verification:</p>
<pre><code class="language-shell">sudo ufw status verbose
</code></pre>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example output:</mark></p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/9f008b51-460e-46a5-a416-e95ebd329811.png" alt="" />

<blockquote>
<p>Note: If SSH is not explicitly allowed before enabling the firewall, all active and future connections will be dropped.</p>
</blockquote>
<hr />
<h3>Service Minimisation</h3>
<p>Every enabled system service increases the memory footprint, dependency surface during boot, and potential attack vectors. There is no distinction between a service you deliberately intended to expose and one installed silently as a dependency unless you actively audit them.</p>
<p>Review your active listeners (<code>sudo ss -tulnp</code>) and disable anything unnecessary:</p>
<pre><code class="language-shell">sudo systemctl disable --now &lt;service_name&gt;
</code></pre>
<h3>The Operational Monitoring Layer</h3>
<p>At this stage, the system is secured but not yet structured for robust observability.</p>
<p>Linux produces vital telemetry, natively split between <strong>runtime event streams</strong> and <strong>persisted file logs</strong> (traditionally in <code>/var/log</code>). Historically, managing this was fragmented. Systemd unifies this execution and monitoring model.</p>
<h3>Why systemd supersedes legacy execution</h3>
<p>Historically, service execution was handled by a mix of SysV init scripts, cron-based scheduling, and ad-hoc supervision tools. This resulted in:</p>
<ul>
<li><p>No unified dependency graph.</p>
</li>
<li><p>Inconsistent startup ordering.</p>
</li>
<li><p>Manual supervision required for long-running processes.</p>
</li>
<li><p>Fragmented logging channels.</p>
</li>
</ul>
<p>Systemd is often reductively called a "service manager", but it is actually a unified execution and dependency management framework. Instead of executing disparate scripts, systemd treats the system as a strict dependency graph of managed units.</p>
<p><strong>Core systemd components:</strong></p>
<ul>
<li><p><strong>PID 1:</strong> The systemd daemon itself, controlling the boot lifecycle.</p>
</li>
<li><p><strong>Unit files:</strong> Declarative service definitions.</p>
</li>
<li><p><strong>Journald:</strong> The integrated, binary log collection subsystem.</p>
</li>
<li><p><strong>Timers:</strong> The scheduled execution model.</p>
</li>
</ul>
<h3>Why systemd timers replace cron</h3>
<p>Cron operates on a rudimentary premise: <em>"<mark class="bg-yellow-200 dark:bg-yellow-500/30">execute command X at time Y</mark>"</em>.<br />It has no contextual awareness of whether the previous execution is still running, if the system is overloaded, or if prerequisite services are available.</p>
<p>Systemd timers resolve these operational blind spots:</p>
<ul>
<li><p><strong>Contextual execution:</strong> Execution is tied strictly to service units, allowing for dependency mapping (e.g., "only run this script if the network is up").</p>
</li>
<li><p><strong>State awareness:</strong> You can query execution state reliably via <code>systemctl</code>.</p>
</li>
<li><p><strong>Built-in supervision:</strong> Overlapping executions can be prevented automatically.</p>
</li>
<li><p><strong>Unified logging:</strong> Standard output (stdout) and errors (stderr) from timed tasks are captured automatically by <code>journald</code>, ensuring scheduled tasks are monitored exactly like persistent daemons.</p>
</li>
</ul>
<p>The shift from cron to systemd timers reflects a move toward system-aware scheduling that integrates with service state, logging, and lifecycle management. Because systemd inherently understands execution state, manages dependencies, and natively captures logs, it provides the robust execution engine required for the active alerting architecture we will implement in <strong>Part Two</strong>.</p>
]]></content:encoded></item><item><title><![CDATA[Production Considerations for Backend Systems]]></title><description><![CDATA[Developing software locally is only one part of the job. Systems must also operate reliably in production, handle failures, and remain maintainable as requirements change. A backend should be understa]]></description><link>https://blog.tech-journey.co.za/production-considerations-for-backend-systems</link><guid isPermaLink="true">https://blog.tech-journey.co.za/production-considerations-for-backend-systems</guid><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Mon, 22 Dec 2025 21:24:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/55b97c25-e9e8-436a-9c97-f2e035942392.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Developing software locally is only one part of the job. Systems must also operate reliably in production, handle failures, and remain maintainable as requirements change. A backend should be understandable by the people responsible for supporting it, both during deployment and months later when issues need to be investigated or new features introduced.</p>
<h3>1. Configuration Management</h3>
<p>Configuration should be separated from application code. Database credentials, API keys, service endpoints, and other environment-specific values should be provided at runtime rather than embedded in source code.</p>
<p>The Twelve-Factor App model describes configuration as external to the application process. In practice, this means values such as database credentials, API keys, and service endpoints are provided at runtime rather than embedded in source code. See <a href="https://12factor.net/">https://12factor.net/</a> for the original reference.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Common problematic patterns include:</mark></strong></p>
<ul>
<li><p>Hardcoded database credentials</p>
</li>
<li><p>API keys committed in source control</p>
</li>
<li><p>Version-controlling <code>.env</code> files</p>
</li>
<li><p>Environment branching inside application logic (for example, <code>if env == "prod"</code>)</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">More stable approaches:</mark></strong></p>
<ul>
<li><p>Inject configuration through environment variables at runtime</p>
</li>
<li><p>Use dedicated secret management systems such as AWS Secrets Manager or HashiCorp Vault for sensitive values</p>
</li>
<li><p>Build a single artifact that is deployed across all environments without modification</p>
</li>
</ul>
<p>The implementation differs across ecosystems. FastAPI applications often use <code>pydantic-settings</code>, Node.js relies on <code>process.env</code> or libraries like <code>dotenv</code>. The mechanism changes, but the model stays the same: configuration is provided externally at runtime.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example (Secret Management):</mark></strong></p>
<p>For secrets, the application typically retrieves credentials from a secret manager during startup:</p>
<pre><code class="language-json">// Request to AWS Secrets Manager
{
  "SecretId": "prod/database/credentials"
}
</code></pre>
<p>The credential is kept in memory for the lifetime of the process and is not stored in source code or committed files.</p>
<hr />
<h3>2. API Design and Idempotency</h3>
<p>API changes affect every consumer of that API. Versioning, backward compatibility, and idempotent operations help reduce operational risk.</p>
<p>Idempotency ensures that repeating the same request doesn't change the system state beyond the first successful execution. This is important in distributed systems where retries are common due to timeouts, network failures, or client-side retry logic.</p>
<p>Without idempotency, retry behaviour can lead to duplicate writes and inconsistent state.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Payment processing with a uniqueness constraint</mark></strong></p>
<p>Each request carries a transaction identifier.</p>
<ul>
<li><p>Client submits a payment with <code>transaction_id = 98765</code></p>
</li>
<li><p>The backend stores the transaction with a UNIQUE constraint on <code>transaction_id</code></p>
</li>
<li><p>If the insert succeeds, the payment is processed</p>
</li>
<li><p>If the insert fails due to a duplicate key, the system treats it as already processed and returns success</p>
</li>
</ul>
<p>The database becomes the enforcement point for preventing duplicate execution.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Idempotency keys with external services</mark></strong></p>
<p>For operations that cannot rely on database constraints, an idempotency key can be used.</p>
<ul>
<li><p>Client sends <code>Idempotency-Key: 123e4567-e89b-12d3-a456-426614174000</code></p>
</li>
<li><p>Backend checks Redis for the key</p>
</li>
<li><p>If the key exists, the cached response is returned</p>
</li>
<li><p>If the key is missing, the operation is executed and the response is stored against the key</p>
</li>
</ul>
<p>This pattern is commonly used for interactions with external services such as email providers or payment gateways.</p>
<hr />
<h3>3. Separation of Responsibilities</h3>
<p>Backend systems tend to be more maintainable when HTTP handling, business logic, and data access are kept distinct, as this reduces coupling between concerns and improves clarity around system boundaries. This aligns with the Single Responsibility Principle (<a href="https://en.wikipedia.org/wiki/Single-responsibility_principle">https://en.wikipedia.org/wiki/Single-responsibility_principle</a>).</p>
<p>A typical structure looks like this:</p>
<ul>
<li><p>Routing layer: Handles HTTP requests, request parsing, validation, and response formatting</p>
</li>
<li><p>Service layer: Contains business logic and application rules</p>
</li>
<li><p>Data access layer: Handles persistence and database queries</p>
</li>
</ul>
<p>Each layer has a clear boundary in terms of responsibility and avoids embedding knowledge of other layers’ implementation details.</p>
<p>With this separation in place, changes tend to stay local. For example, a database migration or a change in persistence strategy doesn't require rewriting HTTP handlers or core business logic, as long as the data access contract remains stable.</p>
<hr />
<h3>4. Observability</h3>
<p>When production systems fail, engineers rely on logs, metrics, and traces to understand what changed and where the failure occurred. The SRE framework describes this through four primary signals (<a href="https://sre.google/sre-book/monitoring-distributed-systems/">https://sre.google/sre-book/monitoring-distributed-systems/</a>).</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Core signals:</mark></strong></p>
<ul>
<li><p>Latency: Time taken to process a request</p>
</li>
<li><p>Traffic: Request volume over time</p>
</li>
<li><p>Errors: Rate of failed requests (4xx and 5xx responses)</p>
</li>
<li><p>Saturation: Resource usage such as CPU, memory, and connection pools</p>
</li>
</ul>
<p>These signals are typically used together rather than in isolation when assessing system health.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Structured logging</mark></strong></p>
<p>Logs are more useful when they are machine-readable rather than plain text. JSON-formatted logs allow consistent parsing across aggregation systems such as Elasticsearch or Datadog.</p>
<pre><code class="language-json">{
  "level": "INFO",
  "event": "order_processed",
  "order_id": 8831,
  "duration_ms": 142,
  "timestamp": "2025-02-03T10:15:00Z"
}
</code></pre>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Distributed tracing</mark></strong></p>
<p>In distributed systems, a single request may pass through multiple services before completing. Tracing systems such as OpenTelemetry propagate a <code>trace_id</code> across each service boundary.</p>
<p>Example flow:</p>
<ul>
<li><p>API Gateway generates <code>trace_id: abc-123</code></p>
</li>
<li><p>User Service receives and forwards the same identifier</p>
</li>
<li><p>Database interactions are recorded under the same trace</p>
</li>
</ul>
<p>All logs associated with the request include the same identifier, allowing engineers to reconstruct the full request path during incident analysis.</p>
<hr />
<h3>Key Takeaways</h3>
<p>Resilient backend systems rely heavily on structural constraints:</p>
<ul>
<li><p>Externalised configuration via environment variables or secret managers.</p>
</li>
<li><p>Contract-driven APIs with enforced idempotency.</p>
</li>
<li><p>Strict architectural boundaries separating routing, business logic, and persistence.</p>
</li>
<li><p>Comprehensive observability utilizing structured logs and distributed tracing.</p>
</li>
</ul>
<p>The implementation details will vary between teams and tech stacks. The goal remains the same: build systems that can be operated, debugged, and changed without unnecessary risk.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding the Software Development Life Cycle (SDLC)]]></title><description><![CDATA[Modern software systems power nearly every aspect of business operations, from banking platforms and healthcare systems to cloud infrastructure, e-commerce applications, and enterprise automation. As ]]></description><link>https://blog.tech-journey.co.za/understanding-the-software-development-life-cycle-sdlc</link><guid isPermaLink="true">https://blog.tech-journey.co.za/understanding-the-software-development-life-cycle-sdlc</guid><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sun, 30 Nov 2025 21:14:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/f5f2593b-d46e-4966-b9f2-2afa1e4e7af9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Modern software systems power nearly every aspect of business operations, from banking platforms and healthcare systems to cloud infrastructure, e-commerce applications, and enterprise automation. As software becomes more deeply integrated into critical services, the process used to design, build, test, deploy, and maintain that software becomes just as important as the code itself.</p>
<p>This is where the Software Development Life Cycle plays a fundamental role.</p>
<p>It is a structured framework used to <strong>plan</strong>, <strong>develop</strong>, <strong>test</strong>, <strong>deploy</strong>, and <strong>maintain</strong> <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">software systems</mark></strong>. It provides a disciplined approach to software engineering that helps organisations deliver reliable, secure, scalable, and maintainable applications while reducing operational risk, technical debt, and project failure.</p>
<p>Rather than treating development as a collection of isolated coding tasks, the SDLC establishes a repeatable process that aligns business requirements, engineering practices, security controls, testing procedures, deployment strategies, and operational maintenance into a cohesive workflow.</p>
<p>Whether building a small internal application or a large-scale enterprise platform, understanding the SDLC is essential for delivering software that is both functional and production-ready.</p>
<hr />
<h3>What is SDLC?</h3>
<p>The Software Development Life Cycle is a methodology that defines the stages involved in developing software systems from initial concept through long-term maintenance.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Its primary objectives are to:</mark></strong></p>
<ul>
<li><p>improve software quality</p>
</li>
<li><p>reduce development risks</p>
</li>
<li><p>increase project visibility</p>
</li>
<li><p>improve collaboration between teams</p>
</li>
<li><p>manage costs and timelines effectively</p>
</li>
<li><p>ensure software aligns with business requirements</p>
</li>
<li><p>support secure and maintainable development practices</p>
</li>
</ul>
<p>The SDLC provides structure and accountability throughout the entire software engineering process.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Without a clearly defined lifecycle, projects often suffer from:</mark></strong></p>
<ul>
<li><p>unclear requirements</p>
</li>
<li><p>inconsistent development practices</p>
</li>
<li><p>poor testing coverage</p>
</li>
<li><p>weak security controls</p>
</li>
<li><p>deployment failures</p>
</li>
<li><p>technical debt</p>
</li>
<li><p>scalability issues</p>
</li>
<li><p>unreliable maintenance processes</p>
</li>
</ul>
<p>The SDLC helps prevent these problems by introducing planning, governance, documentation, validation, and continual improvement throughout development.</p>
<hr />
<h3><strong>How the SDLC Improves Software Delivery Outcomes</strong></h3>
<p>Software failures rarely happen because of code alone. In many cases, <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">problems originate from:</mark></strong></p>
<ul>
<li><p>poor planning</p>
</li>
<li><p>weak communication</p>
</li>
<li><p>incomplete requirements</p>
</li>
<li><p>inadequate testing</p>
</li>
<li><p>rushed deployments</p>
</li>
<li><p>lack of monitoring</p>
</li>
<li><p>missing security controls</p>
</li>
<li><p>insufficient maintenance procedures</p>
</li>
</ul>
<p>The SDLC addresses these operational weaknesses by creating a structured engineering process that supports consistency and quality throughout the lifecycle of an application.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">A mature SDLC helps organisations:</mark></strong></p>
<ul>
<li><p>deliver software more reliably</p>
</li>
<li><p>improve security posture</p>
</li>
<li><p>reduce downtime and production incidents</p>
</li>
<li><p>increase developer productivity</p>
</li>
<li><p>improve collaboration between departments</p>
</li>
<li><p>strengthen compliance and governance</p>
</li>
<li><p>reduce long-term operational costs</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">In enterprise environments, the SDLC is closely tied to:</mark></strong></p>
<ul>
<li><p>DevOps practices</p>
</li>
<li><p>CI/CD pipelines</p>
</li>
<li><p>cloud infrastructure</p>
</li>
<li><p>cybersecurity programmes</p>
</li>
<li><p>observability platforms</p>
</li>
<li><p>change management processes</p>
</li>
<li><p>disaster recovery planning</p>
</li>
</ul>
<hr />
<h3>The Main Phases of the SDLC</h3>
<p>Although implementations vary between organisations, most SDLC models include several core phases.</p>
<h3>1. Planning and Requirements Gathering</h3>
<p>Every successful software project begins with understanding the problem being solved.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">During this phase, stakeholders define:</mark></strong></p>
<ul>
<li><p>business objectives</p>
</li>
<li><p>technical requirements</p>
</li>
<li><p>operational expectations</p>
</li>
<li><p>user needs</p>
</li>
<li><p>compliance obligations</p>
</li>
<li><p>project scope</p>
</li>
<li><p>timelines and budgets</p>
</li>
</ul>
<p>Requirements gathering is one of the most critical stages of the entire lifecycle. <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Poorly defined requirements often lead to:</mark></strong></p>
<ul>
<li><p>project delays</p>
</li>
<li><p>budget overruns</p>
</li>
<li><p>rework</p>
</li>
<li><p>feature creep</p>
</li>
<li><p>architectural problems</p>
</li>
</ul>
<p>Technical teams work closely with business stakeholders to translate operational needs into functional and non-functional requirements.</p>
<p>Functional requirements describe what the application should do.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Examples include:</mark></strong></p>
<ul>
<li><p>user authentication</p>
</li>
<li><p>reporting capabilities</p>
</li>
<li><p>API functionality</p>
</li>
<li><p>workflow automation</p>
</li>
</ul>
<p>Non-functional requirements define how the system should operate.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Examples include:</mark></strong></p>
<ul>
<li><p>performance</p>
</li>
<li><p>scalability</p>
</li>
<li><p>security</p>
</li>
<li><p>reliability</p>
</li>
<li><p>availability</p>
</li>
<li><p>maintainability</p>
</li>
</ul>
<p>Strong requirement analysis significantly reduces downstream engineering problems.</p>
<hr />
<h3>2. System Design and Architecture</h3>
<p>Once requirements are defined, teams begin designing the system architecture.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">This phase focuses on determining:</mark></strong></p>
<ul>
<li><p>application structure</p>
</li>
<li><p>database design</p>
</li>
<li><p>infrastructure requirements</p>
</li>
<li><p>integration points</p>
</li>
<li><p>APIs</p>
</li>
<li><p>authentication mechanisms</p>
</li>
<li><p>security architecture</p>
</li>
<li><p>deployment models</p>
</li>
</ul>
<p>Good architecture decisions have long-term operational impact.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Poor architectural planning can introduce:</mark></strong></p>
<ul>
<li><p>scalability bottlenecks</p>
</li>
<li><p>performance limitations</p>
</li>
<li><p>security vulnerabilities</p>
</li>
<li><p>operational complexity</p>
</li>
<li><p>maintainability issues</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Modern software architecture often incorporates:</mark></strong></p>
<ul>
<li><p>microservices</p>
</li>
<li><p>REST APIs</p>
</li>
<li><p>containerisation</p>
</li>
<li><p>cloud-native infrastructure</p>
</li>
<li><p>distributed systems</p>
</li>
<li><p>event-driven architectures</p>
</li>
</ul>
<p>Security should also be integrated early within the design phase rather than treated as an afterthought.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">This includes:</mark></strong></p>
<ul>
<li><p>threat modelling</p>
</li>
<li><p>access control planning</p>
</li>
<li><p>encryption strategies</p>
</li>
<li><p>secure authentication flows</p>
</li>
<li><p>logging and monitoring requirements</p>
</li>
</ul>
<hr />
<h3>3. Development</h3>
<p>The development phase is where software engineers begin implementing the application.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">This includes:</mark></strong></p>
<ul>
<li><p>writing code</p>
</li>
<li><p>building APIs</p>
</li>
<li><p>creating database schemas</p>
</li>
<li><p>integrating services</p>
</li>
<li><p>configuring infrastructure</p>
</li>
<li><p>developing automation scripts</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Modern development environments rely heavily on</mark></strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark></p>
<ul>
<li><p>version control systems</p>
</li>
<li><p>collaborative workflows</p>
</li>
<li><p>code reviews</p>
</li>
<li><p>branching strategies</p>
</li>
<li><p>automated testing</p>
</li>
<li><p>CI/CD pipelines</p>
</li>
</ul>
<p>Engineering best practices become especially important during this phase.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">These include</mark></strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark></p>
<ul>
<li><p>clean code principles</p>
</li>
<li><p>modular design</p>
</li>
<li><p>separation of concerns</p>
</li>
<li><p>defensive programming</p>
</li>
<li><p>reusable components</p>
</li>
<li><p>documentation standards</p>
</li>
</ul>
<p>Security-focused development practices are equally critical.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Secure coding practices help reduce</mark></strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark></p>
<ul>
<li><p>injection vulnerabilities</p>
</li>
<li><p>insecure authentication</p>
</li>
<li><p>privilege escalation</p>
</li>
<li><p>exposed secrets</p>
</li>
<li><p>insecure dependencies</p>
</li>
<li><p>configuration weaknesses</p>
</li>
</ul>
<p>This is why many organisations now integrate DevSecOps practices directly into the SDLC.</p>
<hr />
<h3>4. Testing and Quality Assurance</h3>
<p>Testing ensures the software behaves as expected under different conditions before deployment into production environments.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">A strong testing strategy helps identify</mark></strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark></p>
<ul>
<li><p>functional defects</p>
</li>
<li><p>performance bottlenecks</p>
</li>
<li><p>security vulnerabilities</p>
</li>
<li><p>integration failures</p>
</li>
<li><p>usability issues</p>
</li>
<li><p>regression problems</p>
</li>
</ul>
<p>Testing is not limited to finding bugs. It is also about validating reliability, stability, and operational readiness.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Common testing types include</mark></strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark></p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Unit Testing</mark></strong>: Tests individual components or functions in isolation.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Integration Testing</mark></strong>: Verifies that multiple services or components work together correctly.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">System Testing</mark></strong>: Evaluates the complete application as a whole.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">User Acceptance Testing (UAT)</mark></strong>: Confirms the application meets business and user requirements.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Performance Testing</mark></strong>: Measures responsiveness, scalability, and resource usage under load.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Security Testing</mark></strong>: Identifies vulnerabilities, misconfigurations, and insecure behaviours.</p>
<p>Modern engineering teams increasingly rely on automated testing frameworks integrated into CI/CD pipelines to improve consistency and reduce deployment risk.</p>
<hr />
<h3>5. Deployment</h3>
<p>Once testing is completed successfully, the application is deployed into production environments.</p>
<p>Deployment processes vary depending on organisational maturity and infrastructure design.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Modern deployment approaches may include:</mark></strong></p>
<ul>
<li><p>container orchestration</p>
</li>
<li><p>infrastructure as code</p>
</li>
<li><p>blue-green deployments</p>
</li>
<li><p>rolling deployments</p>
</li>
<li><p>canary releases</p>
</li>
<li><p>automated rollback mechanisms</p>
</li>
</ul>
<p>Deployment is no longer simply copying files to a server.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Production releases now involve:</mark></strong></p>
<ul>
<li><p>monitoring integrations</p>
</li>
<li><p>observability tooling</p>
</li>
<li><p>security validation</p>
</li>
<li><p>infrastructure provisioning</p>
</li>
<li><p>automated configuration management</p>
</li>
<li><p>compliance checks</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">A poorly managed deployment process can introduce:</mark></strong></p>
<ul>
<li><p>downtime</p>
</li>
<li><p>service instability</p>
</li>
<li><p>data corruption</p>
</li>
<li><p>security exposure</p>
</li>
<li><p>operational disruption</p>
</li>
</ul>
<p>This is why deployment automation and release management have become critical components of modern SDLC practices.</p>
<hr />
<h3>6. Maintenance and Operations</h3>
<p>The SDLC does not end once software is deployed.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Applications require continuous:</mark></strong></p>
<ul>
<li><p>monitoring</p>
</li>
<li><p>patching</p>
</li>
<li><p>optimisation</p>
</li>
<li><p>troubleshooting</p>
</li>
<li><p>scaling</p>
</li>
<li><p>security updates</p>
</li>
<li><p>feature enhancements</p>
</li>
</ul>
<p>Production systems must be maintained throughout their operational lifecycle.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Operational teams monitor:</mark></strong></p>
<ul>
<li><p>system health</p>
</li>
<li><p>performance metrics</p>
</li>
<li><p>application logs</p>
</li>
<li><p>security events</p>
</li>
<li><p>infrastructure utilisation</p>
</li>
<li><p>incident trends</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Maintenance activities often include:</mark></strong></p>
<ul>
<li><p>fixing defects</p>
</li>
<li><p>improving performance</p>
</li>
<li><p>updating dependencies</p>
</li>
<li><p>responding to vulnerabilities</p>
</li>
<li><p>implementing user feedback</p>
</li>
<li><p>adapting to changing business requirements</p>
</li>
</ul>
<p>Long-term software success depends heavily on operational maturity after deployment.</p>
<hr />
<h3>Common SDLC Models</h3>
<p>Different organisations use different SDLC methodologies depending on project complexity, risk tolerance, and delivery requirements.</p>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Waterfall Model</mark></h3>
<p>The Waterfall model follows a sequential structure where each phase is completed before the next begins.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">It is commonly associated with:</mark></strong></p>
<ul>
<li><p>heavy documentation</p>
</li>
<li><p>strict planning</p>
</li>
<li><p>fixed requirements</p>
</li>
<li><p>formal approval processes</p>
</li>
</ul>
<p>Waterfall works best in environments with stable requirements and limited change.</p>
<p>However, it can struggle in fast-changing environments where continuous adaptation is required.</p>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Agile Model</mark></h3>
<p>Agile focuses on iterative development, collaboration, and continuous feedback.</p>
<p>Instead of large, infrequent releases, Agile teams deliver software in smaller increments known as sprints.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Agile encourages:</mark></strong></p>
<ul>
<li><p>rapid iteration</p>
</li>
<li><p>stakeholder collaboration</p>
</li>
<li><p>continuous improvement</p>
</li>
<li><p>adaptive planning</p>
</li>
</ul>
<p>It is widely used in modern software engineering environments.</p>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">DevOps and Continuous Delivery</mark></h3>
<p>DevOps extends the SDLC by integrating development and operations into a unified workflow.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The goal is to improve:</mark></strong></p>
<ul>
<li><p>automation</p>
</li>
<li><p>deployment speed</p>
</li>
<li><p>reliability</p>
</li>
<li><p>collaboration</p>
</li>
<li><p>observability</p>
</li>
<li><p>operational efficiency</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">DevOps practices heavily rely on:</mark></strong></p>
<ul>
<li><p>CI/CD pipelines</p>
</li>
<li><p>infrastructure automation</p>
</li>
<li><p>monitoring systems</p>
</li>
<li><p>containerisation</p>
</li>
<li><p>cloud-native tooling</p>
</li>
</ul>
<p>Modern SDLC implementations increasingly combine Agile and DevOps practices together.</p>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Security in the SDLC</mark></h3>
<p>Security can no longer be treated as a final-stage checklist.</p>
<p>Modern threats require security integration throughout the entire lifecycle.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Secure SDLC practices include:</mark></strong></p>
<ul>
<li><p>threat modelling</p>
</li>
<li><p>secure coding standards</p>
</li>
<li><p>dependency scanning</p>
</li>
<li><p>vulnerability management</p>
</li>
<li><p>secrets management</p>
</li>
<li><p>penetration testing</p>
</li>
<li><p>access control reviews</p>
</li>
<li><p>security monitoring</p>
</li>
</ul>
<p>This approach is commonly referred to as DevSecOps.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">By integrating security earlier into development workflows, organisations reduce:</mark></strong></p>
<ul>
<li><p>security vulnerabilities</p>
</li>
<li><p>remediation costs</p>
</li>
<li><p>compliance risks</p>
</li>
<li><p>production incidents</p>
</li>
</ul>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Challenges in the SDLC</mark></h3>
<p>Even mature organisations face challenges when managing software lifecycles.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Common issues include:</mark></strong></p>
<ul>
<li><p>unclear requirements</p>
</li>
<li><p>poor communication</p>
</li>
<li><p>insufficient testing</p>
</li>
<li><p>weak documentation</p>
</li>
<li><p>unmanaged technical debt</p>
</li>
<li><p>security oversights</p>
</li>
<li><p>inconsistent deployment practices</p>
</li>
<li><p>lack of operational visibility</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Addressing these challenges requires:</mark></strong></p>
<ul>
<li><p>strong engineering culture</p>
</li>
<li><p>automation</p>
</li>
<li><p>governance</p>
</li>
<li><p>cross-functional collaboration</p>
</li>
<li><p>continual improvement</p>
</li>
</ul>
<p>The SDLC is most effective when treated as an evolving operational framework rather than a rigid process.</p>
<hr />
<h3><strong>Conclusion</strong></h3>
<p>The SDLC remains the backbone of how reliable software is designed, built, and maintained in real engineering environments.</p>
<p>Even as tools, frameworks, and delivery models change rapidly, the underlying discipline of structured delivery doesn't disappear. Without it, teams tend to accumulate technical debt faster than they can manage it, and system behaviour becomes harder to reason about over time.</p>
<p>In practice, the SDLC is about enforcing consistency across how software moves from idea to production. When applied well, it reduces uncertainty in delivery, improves code quality, and makes operational issues easier to trace and resolve.</p>
<p>Ultimately, strong SDLC practices are what allow engineering teams to scale systems without losing control of reliability, security, and maintainability as complexity increases.</p>
]]></content:encoded></item><item><title><![CDATA[Systems Thinking & Engineering Decision-Making in Software Development]]></title><description><![CDATA[Most programming content I've come across focuses on syntax, frameworks, or how to build a generic app in ten minutes. That’s great for learning how to replicate a result, but it does absolutely nothi]]></description><link>https://blog.tech-journey.co.za/systems-thinking-engineering-decision-making-in-software-development</link><guid isPermaLink="true">https://blog.tech-journey.co.za/systems-thinking-engineering-decision-making-in-software-development</guid><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sun, 09 Nov 2025 10:43:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/c652ff4e-c6ca-4ab8-ad37-c624f32aafc0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most programming content I've come across focuses on syntax, frameworks, or how to build a generic app in ten minutes. That’s great for learning how to replicate a result, but it does absolutely nothing to build engineering competence.</p>
<p>Real engineering rarely begins in the IDE. It begins with constraints, ambiguity, and trade-offs. The dividing line between a developer who can follow a tutorial and an engineer who can design a resilient system isn’t language proficiency. It is the ability to apply structured thinking under uncertainty.</p>
<hr />
<h4>The "Solution-First" Trap</h4>
<p>A typical failure mode—especially early in a career—is solution-first thinking. You get a jira ticket, open your editor, and you start typing. You pick tools you already know, figure out the design as you go, and patch over edge cases as they blow up in testing.</p>
<p>This approach will eventually produce working software, but the structural damage is already done. Components are <em>tightly coupled.</em> Dependencies are <em>hidden</em>. Testing requires spinning up the entire environment, and the system becomes operationally fragile.</p>
<p>The issue here isn't a lack of coding skill but a premature commitment to a solution. Systems thinking forces you to delay that commitment long enough to actually understand the problem space. It shifts your perspective from seeing a single script or feature to seeing a living set of interacting parts.</p>
<hr />
<h4>Code is Secondary to Data Flow</h4>
<p>Many systems fail not because the logic is flawed, but because the movement of data was never clearly mapped out. If you want to understand a system, <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">follow the data.</mark></strong></p>
<p>Shift your mindset <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">from</mark></strong> <em>“what code do I write?”</em> <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">to</mark></strong> <em>“how does data move through this environment?”</em> You need to be able to explicitly trace <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">where data originates</mark></strong>, <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">how it is transformed</mark></strong>, <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">where it is validated,</mark></strong> and <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">where it exits</mark></strong>.</p>
<p>Take a log analyzer. A junior developer or sysadmin sees it as a script that reads a text file. A systems thinker sees a pipeline: <strong>raw data streams in</strong>, <strong>gets parsed into structured records</strong>, <strong>filtered</strong>, <strong>aggregated into metrics</strong>, <strong>and eventually stored or emitted</strong>. If you can't describe that flow in plain language, your system design isn't stable enough to start coding. Code is just the plumbing used to implement this flow.</p>
<hr />
<h4>Engineering Comes Down to Trade-offs</h4>
<p>A persistent industry myth is that there is always a "best" tool or approach. In reality, engineering is almost entirely about weighing competing constraints. You aren't picking the "best" option, instead, you are choosing the trade-offs you are most willing to live with.</p>
<p>Take a simple file-processing task in Python. You can read the entire file into memory, or you can stream it line-by-line. Neither is universally correct. Reading it into memory is simpler and faster for small files, but it will crash your server if the file is 50GB. Streaming is safer for memory, but adds complexity and might bottleneck I/O. The correct answer is dictated by the context—file size, memory limits, and performance requirements—not by what you prefer writing.</p>
<p>This applies everywhere, even down to standard libraries. Do you use <code>os.path</code> or <code>pathlib</code> in Python? Both work. The decision rests on team conventions, API consistency, and long-term maintainability. An experienced engineer doesn't operate on default settings; they evaluate what the current context demands.</p>
<hr />
<h4>Designing for When Things Break</h4>
<p>Systems thinking means accepting that failure is a feature of the system, not a rare exception to it.</p>
<p>If your code only works under <em>ideal</em> conditions, it is incomplete. Networks partition. Databases lock up. Third-party APIs timeout. Users upload 4GB binaries instead of CSVs. An engineered system defines <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">exactly how </mark></strong> it will behave under these failure states. <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Logging</mark></strong>, <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">structured error handling</mark></strong>, and <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">observability</mark></strong> aren't "nice-to-have" add-ons you bolt on at the end but foundational to the design.</p>
<h4>The Heuristics of System Design</h4>
<p>You don't need a heavy, bureaucratic process to make good engineering decisions. You just need a mental scaffold before you commit to an implementation:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The Actual Problem</mark>:</strong> What are we actually trying to solve? What is explicitly out of scope?</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The Architecture</mark>:</strong> What are the logical components, and how does data pass between them?</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The Cost</mark>:</strong> What dependencies are we introducing? What is the long-term maintenance tax of this approach?</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The Failure State</mark>:</strong> What happens when this breaks? How will we know it broke? How is the blast radius contained?</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">The Scale</mark>:</strong> What happens if volume 10x's tomorrow?</p>
</li>
</ul>
<p>Tools will inevitably change. You will move between Python, C#, Go, Javascript, or whatever comes next. But the necessity to structure problems, evaluate trade-offs, define boundaries, and mitigate failures will <strong>never</strong> go away.</p>
<p>Syntax is temporary. <strong>Systems thinking is permanent</strong>.</p>
]]></content:encoded></item><item><title><![CDATA[REST APIs Through Rugby Systems Thinking]]></title><description><![CDATA[In South African rugby, coordination only works because every participant operates within defined rules. Cheslin Kolbe finishing a counter-attack, Damian Willemse shifting between positions, or Kurt-L]]></description><link>https://blog.tech-journey.co.za/understanding-rest-apis-through-rugby</link><guid isPermaLink="true">https://blog.tech-journey.co.za/understanding-rest-apis-through-rugby</guid><category><![CDATA[REST API]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[json]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Tue, 04 Nov 2025 09:23:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771207318739/c0050ac9-1569-4a80-9972-0c2f8d4f908b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In South African rugby, coordination only works because every participant operates within defined rules. Cheslin Kolbe finishing a counter-attack, Damian Willemse shifting between positions, or Kurt-Lee Arendse exploiting space all depend on shared structure: set phases, agreed signals, and predictable outcomes.</p>
<p>REST APIs operate on the same principle. Systems interact reliably because the rules of communication are strict and well-defined. Once those rules are understood, the system stops looking chaotic.</p>
<hr />
<h3>What is a REST API?</h3>
<p>An API (Application Programming Interface) is a formal contract between systems.</p>
<p>It defines:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">How requests must be structured</mark></strong></p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What responses will look like</mark></strong></p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What operations are permitted</mark></strong></p>
</li>
</ul>
<p>If the request follows the contract, the system responds predictably. If it doesn’t, the request fails early.</p>
<p>This is about a stable interface exposed to consumers.</p>
<hr />
<h3>REST as an Architectural Style</h3>
<p>REST (Representational State Transfer) is a set of constraints for designing APIs over HTTP.</p>
<p>It standardises interaction around:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Resources</mark></strong> (things you operate on)</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Uniform operations </mark></strong> (HTTP methods)</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Stateless communication</mark></strong></p>
</li>
</ul>
<p>Because it uses HTTP, it inherits a widely understood transport mechanism already used by browsers and networks.</p>
<hr />
<h3>API Types (Access Model, Not Behaviour)</h3>
<p>APIs are also classified by who can access them:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Public APIs</mark></strong> – open to external consumers with documentation and keys</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Private APIs</mark></strong> – internal system-to-system communication</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Partner APIs</mark></strong> – restricted access for approved external organisations</p>
</li>
</ul>
<p>This classification is about governance and not technical design.</p>
<hr />
<h3>Resources, URIs, and URLs</h3>
<p>REST models everything as a <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">resource</mark></strong>: a player, a match, or a tournament.</p>
<p><strong>URI (Uniform Resource Identifier)</strong></p>
<p>A URI identifies a resource.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example</mark></strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark></p>
<p><code>player/911</code><br />It is an identifier only. It doesn't define access.</p>
<blockquote>
<p>A URI identifies something but doesn’t tell you <mark class="bg-yellow-200 dark:bg-yellow-500/30">where</mark> or <mark class="bg-yellow-200 dark:bg-yellow-500/30">how</mark> to access it.</p>
</blockquote>
<h3>URL (Uniform Resource Locator)</h3>
<p>A URL identifies and locates the resource over a network.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example:</mark></strong></p>
<p><a href="https://tech-journeys.com/players/kolbe%EF%BF%BCThis"><code>https://tech-journeys.com/players/kolbe</code>  
</a>This URL identifies Cheslin Kolbe and also tells your system to fetch his data via HTTPS from this exact location.</p>
<blockquote>
<p>All URLs are URIs because they identify a resource, but not all URIs are URLs.</p>
</blockquote>
<h3>Quick Rule of Thumb</h3>
<ol>
<li><p><strong>URI:</strong> identifies a resource (who/what)</p>
</li>
<li><p><strong>URL:</strong> identifies a resource <strong>and</strong> tells you how to locate it (who/what + where/how)</p>
</li>
<li><p><strong>Relationship:</strong> Every URL is a URI, but not every URI is a URL.</p>
</li>
</ol>
<h3>Rugby Analogy Summary</h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771213348254/c6ba985c-680b-41e1-ae84-afc55082090d.png" alt="" style="display:block;margin:0 auto" />

<p>Here’s an example written in Python:</p>
<pre><code class="language-python"># Define a list of players (unique identifiers in the system)
player_uris = [
    "players/kolbe",
    "players/smith",
    "players/duplessis",
    "players/mapimpi"
]

# Base URL of our rugby API or website
base_url = "https://tech-journeys.com/"

# Loop through each player and generate their full URL
for uri in player_uris:
    player_url = f"{base_url}{uri}"
    print("Player URI:", uri)
    print("Player URL:", player_url)
    print("-" * 40)  # separator for readability
</code></pre>
<h3>Output</h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771212641833/4cdcc6b0-ade7-4ca9-be88-2b355212f22a.png" alt="" />

<hr />
<h3>HTTP Methods as System Operations</h3>
<p>REST defines standard actions via HTTP methods.</p>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">GET</mark> – Read State</h3>
<p>A <code>GET</code> request <em><strong>asks</strong></em> the server <em><strong>for</strong></em> information without changing anything.</p>
<pre><code class="language-python">import requests

# Get Cheslin Kolbe's stats
response = requests.get("https://tech-journeys.com/players/kolbe") #This URL doesn't really exist :)
player_data = response.json()
print(player_data)
</code></pre>
<h3>Output</h3>
<pre><code class="language-json">{
  "name": "Cheslin Kolbe",
  "team": "South Africa",
  "position": "Wing",
  "special_move": "Ankle Breaker",
  "world_cups": [2019, 2023]
}
</code></pre>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">POST</mark> – Create Resource</h3>
<p>A <code>POST</code> request <em><strong>sends</strong></em> <em><strong>new</strong></em> information to the server to create something that <strong>does not <em>yet</em> exist</strong> like adding a new player to the squad.</p>
<pre><code class="language-python">new_player = {
    "name": "Canan Moodie",
    "team": "South Africa",
    "position": "Centre",
    "world_cups": [2023]
}

response = requests.post("https://tech-journeys.com/players", json=new_player)
print(response.status_code)  # 201 means created
</code></pre>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">PUT</mark> – Updating Resource</h3>
<p>A <code>PUT</code> request <em><strong>replaces</strong></em> or <em><strong>updates</strong></em> an <strong>existing</strong> resource. For example, updating a player’s position after a tactical change.</p>
<pre><code class="language-python">updated_position = {
    "position": "Fullback"
}

response = requests.put("https://tech-journeys.com/players/kolbe", json=updated_position)
print(response.status_code)  # 200 means success
</code></pre>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">DELETE</mark> — Remove Resource</h3>
<p>A <code>DELETE</code> request <em><strong>removes</strong></em> a resource <strong>completely</strong>.</p>
<pre><code class="language-python">response = requests.delete("https://tech-journeys.com/players/retired_player_id")
print(response.status_code)  # 204 means no content, successfully deleted
</code></pre>
<hr />
<h2>A Simple System Design View</h2>
<p>A REST system typically follows a simple flow:</p>
<pre><code class="language-css">Client → API Server → Database
</code></pre>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Client</mark></strong>: browser, mobile app, or script</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">API Server</mark></strong>: request validation, routing, business logic</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Database</mark></strong>: persistent storage of resources</p>
</li>
</ul>
<p>The API server enforces rules and isolates clients from internal complexity.</p>
<hr />
<h3>Data Format: JSON</h3>
<p>APIs exchange data using JSON (JavaScript Object Notation).</p>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example of a player in JSON:</mark></p>
<pre><code class="language-json">{
  "name": "Cheslin Kolbe",
  "team": "South Africa",
  "position": "Wing",
  "special_move": "Ankle Breaker",
  "world_cups": [2019, 2023]
}
</code></pre>
<p>It is structured as key-value pairs and arrays, optimised for machine parsing and human readability.</p>
<hr />
<h2>Security Model</h2>
<p>APIs expose controlled entry points to systems. Without controls, they become attack surfaces.</p>
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Authentication (Identity)</mark></h3>
<p>Confirms <strong>who</strong> is making the request.</p>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">Example:</mark></p>
<pre><code class="language-python">import requests

headers = {"Authorization": "Bearer YOUR_JWT_TOKEN"}
response = requests.get("https://tech-journeys.com/players/kolbe", headers=headers)
print(response.json())
</code></pre>
<p>Common mechanisms:</p>
<ul>
<li><p>API keys</p>
</li>
<li><p>JWT (JSON Web Tokens)</p>
</li>
</ul>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Authorisation (Permissions)</mark></h3>
<p>Determines what the authenticated identity is allowed to do. Identity alone is insufficient.</p>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Encryption (Transport Security)</mark></h3>
<p>HTTPS enforces TLS encryption between client and server. This prevents interception or tampering in transit.</p>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Rate Limiting (Abuse Control)</mark></h3>
<p>APIs enforce request thresholds (e.g., 100 requests/minute) to protect infrastructure from overload and denial-of-service patterns.</p>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Core Mental Model</mark></h3>
<p>REST becomes simpler when reduced to state transitions:</p>
<ul>
<li><p><strong>GET</strong> → observe state</p>
</li>
<li><p><strong>POST</strong> → create state</p>
</li>
<li><p><strong>PUT</strong> → modify state</p>
</li>
<li><p><strong>DELETE</strong> → remove state</p>
</li>
</ul>
<p>Everything else is implementation detail around those transitions.</p>
<hr />
<h2>My approach to complex systems</h2>
<p>Over time, I’ve learned that most systems only feel complex until you relate them to something familiar. Once you do that, the moving parts become easier to reason about.</p>
<p>A <strong>GET</strong> request is just observing state.<br />A <strong>POST</strong> request is introducing change.<br />Access control is no different from checking permissions at a gate.</p>
<p>The specific analogy doesn’t matter. It could be rugby, football, baking, or fixing cars. What matters is mapping abstract behaviour to something you already understand.</p>
<p>That shift in thinking turns technical concepts into something more durable. Instead of memorising terminology, you recognise patterns. And once you recognise the pattern, it’s much easier to apply it when you’re building or debugging real systems.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Programming Logic and System Design]]></title><description><![CDATA[Developers typically become comfortable with syntax first, while structural thinking develops much later.
That gap is rarely visible in small systems. A few functions, a straightforward SQL query, and]]></description><link>https://blog.tech-journey.co.za/understanding-programming-logic-and-system-design</link><guid isPermaLink="true">https://blog.tech-journey.co.za/understanding-programming-logic-and-system-design</guid><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Thu, 30 Oct 2025 20:08:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/4d51a47d-42cc-4c38-b310-95a55922100c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Developers typically become comfortable with syntax first, while structural thinking develops much later.</p>
<p>That gap is rarely visible in small systems. A few functions, a straightforward SQL query, and a single REST endpoint are often sufficient to deliver working features. At that scale, everything still fits neatly together, and the system advances without forcing deeper architectural decisions.</p>
<p>The friction emerges as systems scale. Logic inevitably spreads across API routing, background task workers, database transactions, and scheduled ETL jobs. Small assumptions made early in development begin to interact in unexpected ways. A validation rule added for one endpoint breaks an entirely different asynchronous workflow. A temporary workaround hardens into core system behaviour. Eventually, the codebase becomes difficult to reason about, even for the original authors.</p>
<p>Programming logic and system design is the discipline of structuring software so its behaviour remains predictable as operational complexity scales. It focuses on problem analysis, decision modelling, data flow, and the organization of software into maintainable, isolated components. Languages and frameworks evolve, but the underlying engineering challenges remain constant.</p>
<hr />
<h3>Logic Precedes Implementation</h3>
<p>Implementation is the final stage of a much broader analytical process. Before writing a single line of code, engineers must determine:</p>
<ul>
<li><p>The exact operational responsibilities of the system</p>
</li>
<li><p>The data flow and state dependencies</p>
</li>
<li><p>The conditional evaluation of business rules</p>
</li>
<li><p>The secure-by-default handling of failures</p>
</li>
<li><p>The contract boundaries between components</p>
</li>
<li><p>The systemic constraints that must remain invariant</p>
</li>
</ul>
<p>A high percentage of production defects are entirely disconnected from syntax. They stem from incorrect assumptions, incomplete requirements, poorly modelled state transitions, and hidden dependencies. The programming language merely exposes these logical flaws at runtime.</p>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/f75de7ce-1c6f-49bd-8ce2-75830866c6a0.gif" alt="" style="display:block;margin-left:auto" />

<h3>Defining the Problem Space</h3>
<p>System design starts by eliminating ambiguity at the requirements stage. Business workflows are frequently vague or contradictory. Different operational units often describe identical processes differently based on their specific interactions with the system.</p>
<p>Consider a standard enterprise app: one department considers a record "approved" upon managerial sign-off, while another considers it "approved" only <em><strong>after</strong></em> an ETL pipeline completes financial reconciliation. If this state model is not strictly defined upfront, developers will implement conflicting logic paths throughout the codebase.</p>
<p>Establishing a solid architectural foundation requires defining:</p>
<ul>
<li><p>System boundaries and domain ownership</p>
</li>
<li><p>Actor permissions and responsibilities</p>
</li>
<li><p>Strict inputs, outputs, and validation rules</p>
</li>
<li><p>Expected operational workflows</p>
</li>
<li><p>Clear failure and degradation conditions</p>
</li>
</ul>
<p>Without this rigor, even the cleanest code will degrade into an unstable system.</p>
<hr />
<h3>Core Logic Structures</h3>
<p>Despite the massive scale of modern distributed systems, application logic relies on three foundational control structures:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Sequence</mark>:</strong> The explicit order of execution.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Selection</mark>:</strong> Decision-making and conditional evaluation.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Iteration</mark>:</strong> Repeated execution over data sets or state changes.</p>
</li>
</ul>
<p>Real-world complexity arises from combining these structures across the multiple layers of an enterprise application. An incoming REST API request validates payload data, a service layer evaluates business rules, a database transaction attempts a state mutation, and an asynchronous worker queues downstream notifications.</p>
<p>Each boundary crossed introduces distinct execution paths and potential state transitions. When these paths are not explicitly modelled, the system becomes unpredictable under concurrency or infrastructure stress.</p>
<hr />
<h3>Separation of Concerns and Decomposition</h3>
<p>Large codebases become unmaintainable when responsibilities blur. Tight coupling frequently manifests as business logic leaking into unexpected layers: ORM models, API serializers, middleware, or scheduled scripts.</p>
<p>Initially, this lack of boundary enforcement accelerates feature delivery. Over time, identical logic appears in disparate locations with slight behavioral variations, destroying data consistency.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Decomposition</mark></strong> mitigates this by splitting systems into isolated units with single responsibilities. This practice relies heavily on the Separation of Concerns (SoC), enforcing clear boundaries:</p>
<ul>
<li><p>Isolating API routing from core business logic</p>
</li>
<li><p>Abstracting database access from validation rules</p>
</li>
<li><p>Decoupling infrastructure constraints (like Docker configuration) from application code</p>
</li>
<li><p>Separating read-heavy workflows from write-heavy transactional paths</p>
</li>
</ul>
<p>Effective decomposition drastically improves maintainability, unit test isolation, and operational visibility.</p>
<hr />
<h3>Data Design Dictates System Complexity</h3>
<p>Many architectural bottlenecks are fundamentally data modeling failures disguised as application bugs. When a poor relational or document model is deployed, the application layer is forced to compensate with heavy, repetitive logic:</p>
<ul>
<li><p>Redundant data transformations</p>
</li>
<li><p>Aggressive, defensive input validation</p>
</li>
<li><p>Complex state handling to mask schema deficiencies</p>
</li>
</ul>
<p>Data modeling dictates application architecture. Robust data design requires strict attention to entity relationships, lifecycle management, transactional boundaries, and clear ownership of state mutations. Codebases lacking clear data ownership inevitably spawn concurrency bugs that only surface under heavy production load.</p>
<hr />
<h3>Control Flow and State Management</h3>
<p>Control flow maps how execution traverses a system. In a monolithic, synchronous application, this path is relatively linear. In distributed enterprise systems, execution paths are highly fragmented.</p>
<p>A standard operational flow often involves a FastAPI request triggering validation, committing a PostgreSQL transaction, and publishing a message to a broker for downstream consumption. While the business objective is singular, the underlying execution relies on multiple, independent paths.</p>
<p>This fragmentation is what makes asynchronous architectures challenging to debug. Strong system design makes these execution paths explicit, auditable, and traceable, rather than relying on implied interactions.</p>
<hr />
<h3>Abstraction and Encapsulation</h3>
<p>Abstraction reduces cognitive load by masking implementation details behind stable interfaces. Encapsulation protects internal state by restricting direct access. These concepts are heavily utilized to reduce code duplication (DRY) and isolate change impact.</p>
<p>However, over-abstraction is a persistent hazard in enterprise environments. Teams often engineer complex layers to accommodate hypothetical future requirements, ignoring the YAGNI (You Aren't Gonna Need It) principle. This approach results in deep architectural layering, excessive indirection, and brittle interfaces.</p>
<p>Keeping it simple (KISS) often yields better results. Abstraction is most effective when introduced to solve immediate system friction, rather than to satisfy theoretical design patterns.</p>
<hr />
<h3>Error Handling as Architecture</h3>
<p>Error handling is not a secondary implementation detail; it is a core architectural pillar. A production-grade system requires a defined strategy for handling faults securely and predictably. Engineers must explicitly design for:</p>
<ul>
<li><p>Identifying which operations are safe to retry</p>
</li>
<li><p>Defining strict database rollback conditions</p>
</li>
<li><p>Establishing standardized logging and observability formatting</p>
</li>
<li><p>Ensuring system degradation fails securely, rather than exposing internal state</p>
</li>
</ul>
<p>Inconsistent fault tolerance across microservices destroys operational stability. If one service retries indefinitely, another drops the payload immediately, and a third silently swallows the exception, the aggregate system behavior becomes entirely unpredictable.</p>
<hr />
<h3>Testability as a Structural Metric</h3>
<p>Testing goes beyond quality assurance; it acts as a mirror for architectural health. Code that is notoriously difficult to test usually suffers from deep structural flaws:</p>
<ul>
<li><p>Hidden external dependencies</p>
</li>
<li><p>Tightly coupled infrastructure components</p>
</li>
<li><p>Implicit, undocumented state transitions</p>
</li>
<li><p>Blurred domain boundaries</p>
</li>
</ul>
<p>A well-architected system naturally produces isolated behavioral units, predictable data outputs, and explicit failure triggers. Experienced engineers prioritize testability primarily as a measurement of clean system design.</p>
<hr />
<h3>Conceptual Models Versus Engineering Reality</h3>
<p>Academic models of programming logic often present a clean, linear progression: define the problem, analyze, design, implement, test, and deploy.</p>
<p>In real-world engineering environments, this process is highly iterative and fluid. Requirements shift mid-implementation. Security constraints necessitate immediate redesigns. Production incidents dictate architectural pivots.</p>
<p>The goal of software design principles is not to force a perfect, rigid model onto a chaotic reality. The objective is to manage complexity, define predictable boundaries, and ensure that the codebase remains adaptable, secure, and maintainable long after the initial deployment.</p>
]]></content:encoded></item><item><title><![CDATA[Would You Hire Yourself? The Difference Between Stagnation and Growth]]></title><description><![CDATA[With over eight years in the tech industry, I’ve learned a simple but uncomfortable truth. Effort compounds. The work you put in today pays off later in ways you cannot predict when you’re just starti]]></description><link>https://blog.tech-journey.co.za/the-difference-between-stagnation-and-growth</link><guid isPermaLink="true">https://blog.tech-journey.co.za/the-difference-between-stagnation-and-growth</guid><category><![CDATA[Productivity]]></category><category><![CDATA[Career]]></category><category><![CDATA[networking]]></category><category><![CDATA[jobs]]></category><category><![CDATA[problem solving skills]]></category><category><![CDATA[success]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sat, 11 Oct 2025 16:54:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771460784652/51607bd0-f080-4f7d-bb19-8be8c0081107.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>With over eight years in the tech industry, I’ve learned a simple but uncomfortable truth. Effort compounds. The work you put in today pays off later in ways you cannot predict when you’re just starting out.</p>
<blockquote>
<h3>Credentials open doors. Consistent work determines progression.</h3>
</blockquote>
<p>Degrees, diplomas, and certifications have value, but it is limited. They may help your CV pass initial screening or get you into an interview. Beyond that, they carry little weight on their own.</p>
<p>Once you're in a conversation, the focus shifts to how you reason, how you approach problems, and what you've actually built. Without concrete work to reference, there is little to anchor that discussion, and that gap becomes visible quickly.</p>
<hr />
<h2>No Shortcuts in Competitive Markets</h2>
<p>The job market is tough, and there’s no real bypass around that reality.</p>
<p>A common assumption, especially for graduates or career switchers is that companies will provide structured, in-depth training after hiring. In practice, that expectation often doesn’t match how teams operate.</p>
<p>Attitude does matter, but it has limited weight on its own. Intent is not a substitute for exposure, repetition, or hands-on experience with real systems.</p>
<hr />
<h2>Think Like a Hiring Manager</h2>
<p>From a hiring manager’s perspective, the role exists to offload execution so they can focus on higher-level work and system direction.</p>
<p>In that context, the expectation is rarely to onboard someone from zero. The preference is usually for someone who can contribute meaningfully within a short time frame, even if they're still growing in the role.</p>
<p>Support and guidance are part of the environment, but most positions assume a baseline of practical competence is already in place before day one.</p>
<hr />
<h2>Credentials Without Evidence Have Limited Reach</h2>
<p>If getting interviews or traction is difficult, relying only on qualifications on paper is often not enough. At some point, visibility and demonstrated ability start to matter more than listed credentials.</p>
<p>That visibility comes from consistent output. Whether through writing, short technical notes, videos, or documenting personal projects, the goal is the same: <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">show</mark></strong> how you think, how you approach problems, and how you communicate technical ideas.</p>
<p>Networking and public work are part of engineering practice as they surface how you think, communicate, and apply technical understanding in context that a CV alone cannot carry.</p>
<p>Whether the goal is a first role or progression within an existing one, the underlying principle stays consistent: make your work and reasoning <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">visible</mark></strong>.</p>
<hr />
<h2>Ask the Hard Question</h2>
<p>At some point, self-assessment becomes necessary.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Would you hire yourself for this role, based on what you can demonstrate today?</mark></strong></p>
<p>If the answer is yes, there should be clear, practical reasons you can point to (work you’ve done, problems you’ve solved, and evidence of how you operate).  </p>
<p>If your answer is no, then you’ve just identified a clear roadmap for your <strong>Tech Journey</strong>🙂</p>
<p>Hiring is centred on problem-solving capability. Stating that ability carries little weight on its own. What matters is showing how you approach problems, how you break them down, and whether you can carry that process through consistently.</p>
<hr />
<h2>Documentation Is Part of the Job</h2>
<p>In technical environments, documentation is a requirement of system longevity. People rotate, teams change, but the systems remain.</p>
<p>Good documentation reduces operational risk, improves maintainability, and lowers the cost of future change. The act of writing it also exposes gaps in understanding, making it a form of validation rather than just record-keeping.</p>
<blockquote>
<p><em><mark class="bg-yellow-200 dark:bg-yellow-500/30">Tip</mark></em>: If you are already in a role and want to remain effective over time, avoid outsourcing all reasoning to others. Asking questions is part of the workflow, but doing so without first attempting to understand the system yourself will eventually slow both learning and contribution.</p>
</blockquote>
<hr />
<h2>Experience Doesn’t Automatically Translate to Growth</h2>
<p>Be careful not to confuse tenure with progression. Years of experience can accumulate without a corresponding increase in depth, ownership, or impact.</p>
<p>This often becomes visible when progression is expected but not granted. Being reliable within a defined scope is valuable, but it does not always translate into readiness for broader responsibility.</p>
<p>In many cases, the gap is not time spent in the industry, but the extent to which work has evolved in complexity, scope, and impact.</p>
<hr />
<h2>Use AI Carefully and Intentionally</h2>
<p>Over-reliance on AI to handle so-called tedious tasks can hide a deeper issue. Over time, people stop engaging directly with problems that once forced them to think critically and work through uncertainty. That kind of mental effort is what builds real technical strength.</p>
<p>AI is useful and should be part of the workflow, but as a <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">support tool</mark></strong> rather than a replacement for understanding. Every output still needs to be interpreted, validated, and understood in context. <em><strong>The reasoning behind a solution remains <mark class="bg-yellow-200 dark:bg-yellow-500/30">your responsibility</mark>, regardless of how it was produced.</strong></em></p>
<hr />
<h2>You Don’t Need Expensive Tools to Learn Properly</h2>
<p>Homelabbing since 2020 has made one thing clear: effective learning is not dependent on premium software or high-end hardware. Constraints often force a better understanding of fundamentals.</p>
<p>Free tiers and open-source tools are widely used in industry environments. They expose the same underlying concepts as enterprise platforms, which makes the skills <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">transferable</mark></strong> across stacks.</p>
<p>Tools such as Git, GitHub, Linux, Docker, Nginx, Python, Bash, Prometheus, Grafana, the ELK stack, n8n, Proxmox, and others can all be learned without financial cost. Some require account creation, while others can be installed and run locally for experimentation and practice.</p>
<p>The focus is not the tool itself, but the problem it solves and the system behaviour it represents.</p>
<hr />
<h2>Stop Waiting and Start Building</h2>
<p>At some point, planning stops being productive. Prolonged preparation without execution leads to stagnation while others gain experience through repetition and real work.</p>
<p>If there is intent to get serious about learning, it often requires practical trade-offs. That can include saving for an affordable, second-hand, upgradeable machine. Appearance is irrelevant. Stability and usability matter more than specifications or aesthetics.</p>
<p>Once a usable setup is in place, the workflow becomes straightforward: set up a lab, build and deploy something functional (<strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">don't worry about perfection</mark></strong>), break it, recover it, and document the process. That cycle is where understanding develops.</p>
<blockquote>
<p><mark class="bg-yellow-200 dark:bg-yellow-500/30">NB</mark>: Avoid switching between tools or technologies without direction. Constantly chasing new stacks creates surface-level familiarity without depth. Sustained progress comes from selecting a small set of relevant tools and working with them long enough to understand their behaviour in real scenarios.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Understanding ITIL v4: A Modern Framework for IT Service Management]]></title><description><![CDATA[Organisations today rely heavily on technology to deliver services, support operations, and remain competitive. As IT environments become more complex, businesses need a structured approach to managin]]></description><link>https://blog.tech-journey.co.za/a-comprehensive-guide-to-itil-v4</link><guid isPermaLink="true">https://blog.tech-journey.co.za/a-comprehensive-guide-to-itil-v4</guid><category><![CDATA[ITIL]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Thu, 28 Aug 2025 19:17:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/60c2bacd-6ee4-40a9-869c-f0f6499f08de.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Organisations today rely heavily on technology to deliver services, support operations, and remain competitive. As IT environments become more complex, businesses need a structured approach to managing services efficiently while still adapting to constant change. This is where ITIL v4 comes in.</p>
<p>ITIL v4, short for Information Technology Infrastructure Library version 4, is a globally recognised framework for IT service management (ITSM). It provides organisations with a practical set of best practices for designing, delivering, supporting, and continually improving IT services. Rather than focusing purely on technology, ITIL v4 emphasises aligning IT services with broader business objectives to create measurable value for both organisations and customers.</p>
<p>Unlike earlier versions, ITIL v4 embraces modern ways of working by integrating concepts from Agile, DevOps, and Lean methodologies. This makes the framework far more adaptable to modern digital environments, where speed, collaboration, automation, and continuous improvement are essential.</p>
<hr />
<h4>What Makes ITIL v4 Different?</h4>
<p>One of the biggest changes introduced in ITIL v4 is its focus on value co-creation. Instead of viewing IT as a separate support function operating in isolation, ITIL v4 recognises that value is created through collaboration between IT teams, business departments, customers, suppliers, and other stakeholders.</p>
<p>This shift reflects the reality of modern organisations, where successful service delivery depends on cross-functional cooperation rather than siloed teams and rigid processes.</p>
<p>ITIL v4 also moves away from heavily process-driven thinking and introduces a more flexible, adaptable operating model that can support organisations of any size or industry.</p>
<hr />
<h3>The Core of ITIL v4: The Service Value System (SVS)</h3>
<p>At the centre of ITIL v4 is the Service Value System (SVS). The SVS explains how all components of an organisation work together to create value through IT-enabled services.</p>
<p>Rather than treating service management as a collection of isolated activities, the SVS provides a holistic view of how governance, practices, processes, people, and technology interact across the organisation.</p>
<p>The Service Value System consists of several <strong>key elements</strong>:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Guiding Principles</mark></strong>: These are universal recommendations that help organisations make informed decisions regardless of their structure, goals, or operating model. The principles encourage flexibility, practicality, and continual improvement.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Governance</mark></strong>: This component ensures that organisational policies, priorities, and objectives are properly aligned with service management activities. It provides oversight, accountability, and direction from leadership.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Service Value Chain</mark></strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">:</mark> Acts as the operational backbone of ITIL v4. It outlines the activities required to transform demand into value-producing services.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Practices</mark></strong>: ITIL v4 replaces the older concept of rigid processes with 34 flexible management practices that organisations can adapt to suit their own environments.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Continual Improvement</mark></strong>: Continuous improvement is embedded throughout the framework. Organisations are encouraged to regularly evaluate services, identify opportunities for enhancement, and respond to changing business needs.</p>
</li>
</ul>
<hr />
<h3>2. The Seven Guiding Principles of ITIL v4</h3>
<p>The guiding principles are designed to help organisations apply ITIL effectively in real-world situations. These principles are designed to be practical and applicable in any situation:</p>
<ol>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Focus on Value</mark></strong>: Every activity, process, or improvement initiative should contribute directly or indirectly to creating value for customers and stakeholders.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Start Where You Are</mark></strong>: Rather than rebuilding systems from scratch, organisations should assess existing capabilities and improve what already works.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Progress Iteratively with Feedback</mark></strong>: Large-scale transformations are often risky and difficult to manage. ITIL v4 encourages smaller, incremental improvements supported by regular feedback.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Collaborate and Promote Visibility</mark></strong>: Effective communication and transparency help eliminate silos and improve decision-making across teams.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Think and Work Holistically</mark></strong>: Services do not operate independently. Organisations must consider how people, processes, technology, suppliers, and workflows interact as part of a larger system.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Keep It Simple and Practical</mark></strong>: Complexity should only exist where it adds genuine value. Unnecessary bureaucracy and over-engineering reduce agility and efficiency.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Optimise and Automate</mark></strong>: Manual processes should be streamlined wherever possible, with automation used strategically to improve consistency, reliability, and operational efficiency.</p>
</li>
</ol>
<hr />
<h3>3. Understanding the Service Value Chain</h3>
<p>The <strong>Service Value Chain (SVC)</strong> is a flexible operating model within the Service Value System. It defines how different activities combine to deliver valuable products and services.</p>
<p>The SVC consists of six interconnected activities:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Plan</mark></strong>: This activity focuses on understanding organisational objectives, assessing current capabilities, and defining improvement strategies.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Improve</mark></strong>: Continuous improvement is applied across all services, processes, and practices to enhance performance and efficiency.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Engage</mark></strong>: Strong relationships with customers, users, suppliers, and stakeholders are essential for understanding expectations and maintaining alignment.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Design and Transition</mark></strong>: New or updated services are designed, tested, and transitioned into production while ensuring quality, security, cost-effectiveness, and reliability.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Obtain/Build</mark></strong>: Resources required for service delivery — including software, infrastructure, tools, and personnel — are acquired or developed during this stage.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Deliver and Support</mark></strong>: This activity ensures services are delivered effectively and supported through functions such as incident management, request fulfilment, and user support.</p>
</li>
</ul>
<p>The flexibility of the Service Value Chain allows organisations to adapt workflows depending on the nature of the service or business requirement.</p>
<hr />
<h3>4. ITIL v4 Practices</h3>
<p>ITIL v4 introduces <strong>34 management practices</strong> that provide practical guidance for managing services across the organisation. These practices are grouped into three categories.. They are grouped into <strong>three</strong> main categories:</p>
<ol>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">General Management Practices</mark></strong>: These practices originate from broader business disciplines and are adapted for service management. Examples include:</p>
<ul>
<li><p>Information Security Management</p>
</li>
<li><p>Risk Management</p>
</li>
<li><p>Continual Improvement</p>
</li>
<li><p>Change Enablement</p>
</li>
<li><p>Project Management.</p>
</li>
</ul>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Service Management Practices</mark></strong>: These practices are specifically focused on IT service management and operational support, including:</p>
<ul>
<li><p>Incident Management</p>
</li>
<li><p>Problem Management</p>
</li>
<li><p>Service Desk</p>
</li>
<li><p>Service Level Management</p>
</li>
<li><p>Service Request Management</p>
</li>
</ul>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Technical Management Practices</mark></strong>: These practices focus on technical operations and infrastructure management, such as:</p>
<ul>
<li><p>Infrastructure and Platform Management</p>
</li>
<li><p>Deployment Management</p>
</li>
<li><p>Software Development and Management</p>
</li>
</ul>
</li>
</ol>
<p>Unlike traditional process-heavy frameworks, ITIL v4 practices are designed to be flexible and adaptable rather than prescriptive.</p>
<hr />
<h3>How Organisations Implement ITIL v4</h3>
<p>Implementing ITIL v4 is not a one-time project or certification exercise. It is an ongoing journey focused on operational maturity and continual improvement.</p>
<p>Most organisations follow several broad stages during adoption. It typically involves a few key steps:</p>
<ol>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Assessment and Gap Analysis</mark></strong>: The organisation evaluates its current service management capabilities, identifies inefficiencies, and determines areas requiring improvement.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Training and Awareness</mark></strong>: Teams are introduced to ITIL concepts, terminology, and practices to establish a shared understanding across the business.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Adopting relevant practices</mark></strong>: Rather than implementing every ITIL practice at once, organisations typically prioritise the practices most relevant to their operational needs.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Integration with Existing Frameworks</mark></strong>: ITIL v4 integrates well with Agile, DevOps, cloud operations, cybersecurity frameworks, and modern software delivery practices.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Continual Improvement</mark></strong>: Metrics, feedback, incident trends, service performance data, and stakeholder input are continuously reviewed to identify optimisation opportunities.</p>
</li>
</ol>
<hr />
<p>Modern organisations operate in environments where downtime, poor service delivery, and operational inefficiencies can have significant financial and reputational consequences.</p>
<p>ITIL v4 helps organisations:</p>
<ul>
<li><p>Align IT services with business objectives</p>
</li>
<li><p>Improve operational efficiency</p>
</li>
<li><p>Reduce service disruptions</p>
</li>
<li><p>Strengthen collaboration across teams</p>
</li>
<li><p>Improve customer satisfaction</p>
</li>
<li><p>Support digital transformation initiatives</p>
</li>
<li><p>Enable scalable and repeatable service delivery</p>
</li>
<li><p>Build a culture of continual improvement</p>
</li>
</ul>
<p>Its flexibility also makes it suitable for organisations adopting cloud computing, automation, DevOps pipelines, hybrid infrastructure, and modern service delivery models.</p>
<hr />
<h3>Conclusion</h3>
<p>ITIL v4 represents a major evolution in IT service management. By combining proven operational practices with modern approaches such as Agile, Lean, and DevOps, it provides organisations with a practical framework for delivering reliable, value-driven IT services in rapidly changing environments.</p>
<p>Rather than enforcing rigid processes, ITIL v4 encourages adaptability, collaboration, automation, and continuous improvement. For organisations seeking to modernise operations, improve service quality, and better align technology with business goals, ITIL v4 remains one of the most effective and widely adopted frameworks available today.</p>
]]></content:encoded></item><item><title><![CDATA[If It Isn’t Documented, It Doesn’t Exist]]></title><description><![CDATA[If you’ve ever spent hours deciphering someone else’s cryptic configuration, or frantically tried to recall that one crucial step you performed months ago, you’ll understand the silent agony of poor (]]></description><link>https://blog.tech-journey.co.za/importance-of-documentation</link><guid isPermaLink="true">https://blog.tech-journey.co.za/importance-of-documentation</guid><category><![CDATA[Technical writing ]]></category><category><![CDATA[documentation]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Tue, 05 Aug 2025 18:01:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772362240/52744ff4-1314-421c-a78b-10641b057cf4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you’ve ever spent hours deciphering someone else’s cryptic configuration, or frantically tried to recall that one crucial step you performed months ago, you’ll understand the silent agony of poor (or non-existent) documentation. Just like overthinking can stall our progress, the lack of clear documentation can cripple our teams and hinder our individual growth.</p>
<p>In the world of <strong>IT,</strong>  whether we’re on the front lines of helpdesk support, managing critical infrastructure, building automation pipelines, or writing integrations, it’s easy to prioritise <em><strong>doing</strong></em> over <em><strong>explaining</strong></em>. We troubleshoot, we fix, we deploy, and we move on. Documentation often feels like red tape, a slowdown we can’t afford.</p>
<p>But what if documentation isn’t a burden? What if it’s a <em>force multiplier</em> i.e, a way to increase the value of the work we’ve already done?</p>
<hr />
<h3>What Good Documentation Looks Like</h3>
<p>Good documentation isn’t about writing War and Peace for every single task. It’s about creating clear, concise, and easily accessible resources that help ourselves and our colleagues understand:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What was done</mark>:</strong> The exact steps taken to configure a system, resolve an issue, or implement a change.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Why it was done</mark>:</strong> The reasoning behind decisions, the context of the problem, and the intended outcome.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">How it was done</mark>:</strong> The specific tools, commands, and configurations used.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Where to find it</mark>:</strong> A centralised and organised system for storing and retrieving information.</p>
</li>
</ul>
<hr />
<h3>A Real-World Process for Writing Better Documentation</h3>
<blockquote>
<p>Creating documentation isn’t about perfection, it’s more about <strong>clarity</strong>, <strong>consistency</strong>, and <strong>actionability</strong>. The flow below outlines a practical approach I use to create high-impact, team-friendly documentation.</p>
</blockquote>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772359680/75721d4b-c1a4-4799-b28c-92d32c52ec95.gif" alt="This diagram was created using draw.io" />

<p><strong>Documentation can take many forms:</strong></p>
<ul>
<li><p>Knowledge base articles.</p>
</li>
<li><p>How-to guides.</p>
</li>
<li><p>Workflows.</p>
</li>
<li><p>Troubleshooting guides.</p>
</li>
<li><p>Release notes.</p>
</li>
<li><p>Onboarding/Off-boarding processes.</p>
</li>
<li><p>User manuals.</p>
</li>
<li><p>Infrastructure diagrams and playbooks for operations teams.</p>
</li>
</ul>
<p>The key is to tailor the format and depth to <strong>suit the <em>audience and purpose</em>   without compromising clarity</strong>.</p>
<hr />
<h3>The Hidden Power of Documented Processes</h3>
<p>Just as <a href="https://medium.com/@luqmaanmarthinus93/from-overthinking-to-execution-confronting-analysis-paralysis-in-tech-18ee46701e2d">analysis paralysis</a> can stem from a desire to “get it right,” the reluctance to document can sometimes come from a feeling that “it’s faster if I just do it myself.” While this might be true in the short term, it creates <strong>significant long-term risks</strong>.</p>
<h3><strong>Preventing the Single Point of Failure:</strong></h3>
<p>One of the most critical benefits of thorough documentation is its ability to <strong>mitigate</strong> the risk of <strong>a single point of failure</strong>. Imagine a scenario where only one person on your team knows how to troubleshoot a critical system or perform a key deployment. What happens when that person is unavailable due to illness, vacation, or a change in employment?</p>
<p>Suddenly, the team is scrambling, productivity grinds to a halt, and stress levels skyrocket. This is where documentation acts as a vital safety net. By clearly outlining processes and solutions, we empower the entire team to:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Troubleshoot independently</mark>:</strong> When issues arise, well-documented procedures allow other team members to diagnose and resolve problems without relying solely on the “expert.”</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Onboard new team members efficiently</mark>:</strong> Comprehensive documentation provides a valuable resource for new hires to quickly get up to speed on systems and processes.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Maintain consistency</mark>:</strong> Documented standards and procedures ensure that tasks are performed consistently, reducing errors and improving reliability.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Share knowledge and learn from each other</mark>:</strong> Documentation becomes a living repository of team knowledge, fostering collaboration and continuous learning.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Reduce reliance on tribal knowledge</mark>:</strong> Tacit knowledge held only by individuals is a significant risk. Documentation helps to codify this knowledge and make it accessible to everyone.</p>
</li>
</ul>
<hr />
<h3>My Journey Towards Documentation Discipline</h3>
<p>Like many, I used to view documentation as a necessary evil. It felt time-consuming and often got pushed to the bottom of the priority list. However, after experiencing firsthand the chaos and frustration caused by a lack of documentation, I’ve come to see it as an indispensable part of effective IT practice.</p>
<p>I’ve started making a conscious effort to document as I go, even for seemingly small tasks. I’ve also championed the creation of a centralised knowledge base for my team. The initial investment of time has already paid dividends in terms of reduced support requests, faster troubleshooting, and a more resilient team.</p>
<hr />
<h3>A Call to Document: Empower Yourself and Your Team</h3>
<p>It’s time we shift our perspective on documentation because it’s an investment in our collective knowledge, our team’s resilience, and our own future success.</p>
<p>Just as we challenged ourselves to embrace “comfortable incompletion” in the pursuit of progress, let’s commit to embracing consistent and clear documentation. It’s a simple yet powerful way to prevent bottlenecks, empower our colleagues, and ensure that the knowledge we gain doesn’t walk out the door with any single individual.</p>
<p>Start small. Document one key process this week. Share your knowledge. You’ll be surprised by the positive impact it has on yourself and your team.🙂</p>
]]></content:encoded></item><item><title><![CDATA[Desktop vs Laptop vs Server: Understanding System Roles & Real-World Trade-offs]]></title><description><![CDATA[In computing, the terms desktop, laptop, and server are often used loosely, as if they’re interchangeable. But like comparing a sports car, a motorcycle, and a heavy-duty truck, each is engineered for]]></description><link>https://blog.tech-journey.co.za/desktop-laptop-or-server</link><guid isPermaLink="true">https://blog.tech-journey.co.za/desktop-laptop-or-server</guid><category><![CDATA[proxmox]]></category><category><![CDATA[Homelab]]></category><category><![CDATA[self-hosted]]></category><category><![CDATA[server]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sun, 06 Jul 2025 16:36:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772473873/b235cb42-b4d2-41d2-bada-9a08e8823c7b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In computing, the terms <em>desktop</em>, <em>laptop</em>, and <em>server</em> are often used loosely, as if they’re interchangeable. But like comparing a sports car, a motorcycle, and a heavy-duty truck, each is engineered for a fundamentally different purpose.</p>
<p>This guide breaks down those differences clearly, and more importantly, shows why an old desktop or laptop can still become a powerful asset in a home lab.</p>
<h2><strong>Desktops: The Personal Productivity Machine</strong></h2>
<p><strong>What is a Desktop?</strong><br />A desktop computer is designed for individual use. It’s your daily driver for tasks like browsing, editing documents, gaming, content creation, or video conferencing. It prioritises user experience, responsiveness, and visual performance.</p>
<h3><strong>Typical Hardware Features:</strong></h3>
<ul>
<li><p><strong>CPU</strong>: High-clock-speed processors like Intel Core i5/i7/i9 or AMD Ryzen  which is great for single-threaded workloads</p>
</li>
<li><p><strong>RAM</strong>: 8–32GB (non-ECC), optimised for <strong>general</strong> productivity</p>
</li>
<li><p><strong>Storage</strong>: Fast SSDs for OS/apps + optional HDDs for data. Usually no RAID</p>
</li>
<li><p><strong>GPU</strong>: Often dedicated, supporting gaming, design, and media workloads</p>
</li>
<li><p><strong>Networking</strong>: Single Gigabit Ethernet port and Wi-Fi</p>
</li>
<li><p><strong>Redundancy</strong>: Minimal—hardware failure usually means downtime</p>
</li>
<li><p><strong>Form Factor</strong>: Towers, small form factor (SFF), or all-in-one</p>
</li>
<li><p><strong>Operating Systems</strong>: Windows, macOS, or Linux distros with a GUI</p>
</li>
</ul>
<h3><strong>Common Use Cases</strong></h3>
<ul>
<li><p>Browsing, email and office work</p>
</li>
<li><p>Gaming and creative content</p>
</li>
<li><p>Media streaming and light personal hosting</p>
</li>
<li><p>Development and learning environments</p>
</li>
</ul>
<hr />
<h2>Laptops: Portable, But Compromised</h2>
<p><strong>What is a Laptop?</strong></p>
<p>A laptop delivers desktop-like functionality in a portable form. It’s designed for mobility, not sustained performance or long-term uptime.</p>
<h3><strong>Hardware Characteristics</strong></h3>
<ul>
<li><p><strong>CPU:</strong> Power-efficient versions of desktop processors</p>
</li>
<li><p><strong>RAM:</strong> Typically 8–32GB, often soldered or limited in upgradeability</p>
</li>
<li><p><strong>Storage:</strong> Usually a single SSD/NVMe slot</p>
</li>
<li><p><strong>GPU</strong>: Integrated or lower-tier dedicated GPU.</p>
</li>
<li><p><strong>Networking</strong>: Wi-Fi primary, <em>sometimes</em> Ethernet (less common in newer models).</p>
</li>
<li><p><strong>Redundancy</strong>: None.</p>
</li>
<li><p><strong>Battery</strong>: Built-in UPS equivalent, but not suited for continuous 24/7 operation</p>
</li>
<li><p><strong>Form Factor:</strong> Compact, all-in-one design</p>
</li>
<li><p><strong>Operating Systems:</strong> Same as desktops</p>
</li>
</ul>
<h3><strong>Common Use Cases</strong></h3>
<ul>
<li><p>Mobile productivity and remote work</p>
</li>
<li><p>Learning and experimentation</p>
</li>
<li><p>Remote administration of servers</p>
</li>
<li><p>Light-duty home lab workloads</p>
</li>
</ul>
<hr />
<h2><strong>Servers: Built for Reliability &amp; Scale</strong></h2>
<p><strong>What is a Server?</strong><br />A server is purpose-built to deliver services to other systems over a network. It’s designed for continuous operation, high reliability, and scalability under load. This is where critical infrastructure lives—web apps, databases, authentication systems, and more.</p>
<h3>Typical Hardware Characteristics</h3>
<ul>
<li><p><strong>CPU:</strong> Multi-core, multi-socket processors (Intel Xeon, AMD EPYC)</p>
</li>
<li><p><strong>RAM:</strong> 64GB to 1TB+ with ECC for data integrity</p>
</li>
<li><p><strong>Storage:</strong> RAID arrays, hot-swappable drives, enterprise-grade disks</p>
</li>
<li><p><strong>GPU:</strong> Minimal or none unless required for specialised workloads</p>
</li>
<li><p><strong>Networking:</strong> Multiple NICs, often 10GbE+, with redundancy and bonding</p>
</li>
<li><p><strong>Redundancy:</strong> Dual PSUs, redundant cooling, failover systems</p>
</li>
<li><p><strong>Form Factor:</strong> Rack-mounted or enterprise towers (loud, heavy, and built for data centres)</p>
</li>
<li><p><strong>Operating Systems:</strong> Ubuntu Server, Debian, RHEL, and others<br /><strong>Note</strong>: I’m <strong>not</strong> a big Windows fan so you won’t be seeing that much content relating to Windows OS, you’re <strong>most</strong> welcome 😄).</p>
</li>
</ul>
<h3>Common Use Cases</h3>
<ul>
<li><p>Hosting web applications and APIs</p>
</li>
<li><p>Running databases and storage systems</p>
</li>
<li><p>Virtualisation and container platforms</p>
</li>
<li><p>Identity services (LDAP/AD), backups, and monitoring</p>
</li>
</ul>
<hr />
<h2>The Key Difference</h2>
<p>A desktop or laptop is built for <em>one user, interactive workloads, and visual output</em>.<br />A server is built for <em>many users, continuous workloads, and service delivery</em>.</p>
<h3>Turning Desktops and Laptops into Home Lab Servers</h3>
<p>I came to find that in resource-constrained environments, repurposing a desktop or laptop as a home lab server is a practical and effective approach which I've been doing since 2020.</p>
<ul>
<li><p><strong>Low upfront cost:</strong> Leverage hardware you already own instead of investing in new equipment (at least for now)</p>
</li>
<li><p><strong>Reduced power consumption:</strong> More efficient than enterprise gear, especially where electricity costs are high</p>
</li>
<li><p><strong>Hands-on learning:</strong> Ideal for building real-world skills with Linux, containerisation, and virtualisation platforms such as Docker and Proxmox</p>
</li>
</ul>
<h2>Practical Repurposing Tips</h2>
<p>When converting a desktop or laptop into a server, treat it like a production system with the following best practices:</p>
<p><strong>Optimisation</strong></p>
<ul>
<li><p>Remove unnecessary peripherals (webcams, audio devices)</p>
</li>
<li><p>Disable unused hardware in BIOS</p>
</li>
<li><p>Replace GPU (if unused): Lower idle power, less heat</p>
</li>
</ul>
<p><strong>Power &amp; Stability</strong></p>
<ul>
<li><p>Use a UPS  especially in power-unstable regions(<strong>load shedding</strong>)</p>
</li>
<li><p>Configure automatic power-on after outages in BIOS</p>
</li>
<li><p>Clean regularly  as dust kills airflow and shortens lifespan</p>
</li>
</ul>
<p><strong>Cooling &amp; Noise</strong></p>
<ul>
<li><p>Improve airflow or run side-panel open</p>
</li>
<li><p>Monitor thermals under load</p>
</li>
</ul>
<p><strong>Understand the Limitations</strong></p>
<ul>
<li><p>Software RAID instead of hardware.</p>
</li>
<li><p>Accept lack of ECC RAM  -  mitigate with backups.</p>
</li>
<li><p>No hot-swap? Keep backups and spare parts.</p>
</li>
</ul>
<p><strong>Final Thoughts</strong><br />A desktop/laptop is your personal workstation, responsive and graphics-ready. A server is designed for non-stop, multi-user workloads. But when approached intelligently(<strong>do your research</strong>), a desktop/laptop can be used as a lab-grade server  giving you a platform to learn, experiment, and even deploy services that you’d use in the real-world.</p>
<hr />
<h3>A Real Example</h3>
<p>This entire philosophy isn’t theoretical.</p>
<p>Below are some pictures of a Dell Latitude 5500 (8th Gen Core i5), that I purchased in <strong>July 2024</strong> for just 750 ZAR, has been running Proxmox VE reliably ever since.</p>
<p>It started with:</p>
<ul>
<li><p>256GB NVMe SSD</p>
</li>
<li><p>4GB RAM</p>
</li>
</ul>
<p>And was gradually upgraded to:</p>
<ul>
<li><p>512GB NVMe SSD</p>
</li>
<li><p>32GB RAM</p>
</li>
</ul>
<p>It’s neither pretty (or is it? check the last picture), nor does it look like a “real” server. However, it works and that's what matters most.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772470426/f1990c8c-c1eb-44ef-843c-b6e2859b331b.jpeg" alt="" />

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772472104/5d697182-62b7-41de-a921-1b1ff7a05dee.jpeg" alt="" />

<h3>My creative work</h3>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/5159531f-7803-4892-96fc-1b0b274b7a5f.jpg" alt="" style="display:block;margin:0 auto" />

<hr />
<p>In constrained environments—whether limited by budget, power, or access to enterprise hardware—waiting for the “ideal” setup only slows progress.</p>
<p>Start with what you have.<br />Observe how systems behave under real conditions.<br />Build something that works.</p>
<p>That repurposed machine on your desk may not look like enterprise infrastructure—but the experience you gain from running, breaking, and improving it is far more valuable than idle hardware sitting in a rack.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Virtualisation and Containerisation]]></title><description><![CDATA[In the ever-evolving landscape of IT, the quest for efficiency, scalability, and resource optimisation has led to the widespread adoption of two powerful technologies: virtualisation and containerisat]]></description><link>https://blog.tech-journey.co.za/understanding-virtualisation-and-containerisation</link><guid isPermaLink="true">https://blog.tech-journey.co.za/understanding-virtualisation-and-containerisation</guid><category><![CDATA[containers]]></category><category><![CDATA[containerization]]></category><category><![CDATA[virtualization]]></category><category><![CDATA[virtual machine]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sat, 21 Jun 2025 16:46:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772369909/2dee21d4-5e6e-46dc-8a2e-0524dcaf0e6d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the ever-evolving landscape of IT, the quest for efficiency, scalability, and resource optimisation has led to the widespread adoption of two powerful technologies: <strong>virtualisation</strong> and <strong>containerisation</strong>. While often discussed in the same breath, they represent distinct approaches to abstracting applications and their environments, each with its unique strengths and use cases. Let’s delve into what they are, the problems they solve, and their fundamental differences.</p>
<hr />
<h3><strong>Virtualisation: The Power of the Virtual Machine</strong></h3>
<p><strong>What it is</strong>:<br />At its core, virtualisation is the technology that allows you to create <strong>software-based</strong>, or “<strong>virtual</strong>,” versions of computing resources, such as servers, storage devices, networks, and even operating systems. Imagine having a single powerful physical server, and being able to run multiple, independent “virtual machines” (VMs) on it, each behaving like a completely separate physical computer.</p>
<p>This magic is performed by a piece of software called a <a href="https://aws.amazon.com/what-is/hypervisor/">hypervisor</a>. The hypervisor sits directly on the physical hardware (Type-1, or bare-metal, like Proxmox VE(my favorite) or VMware ESXi) or on top of an existing operating system (Type-2, or hosted, like VirtualBox or VMware Workstation). It allocates the physical resources (CPU, RAM, storage, network) to each VM and manages their execution, ensuring they run in isolation from each other. Each VM includes its own operating system (the “guest OS”) and applications.</p>
<p><strong>Problems it solves</strong>:<br />Before virtualisation, deploying a new application often meant dedicating a physical server to it which led to:</p>
<ul>
<li><p><strong>Underutilisation of hardware:</strong> Most servers are not constantly running at 100% capacity, meaning significant computational power was often wasted.</p>
</li>
<li><p><strong>Server sprawl:</strong> A growing number of physical servers meant increased power consumption, cooling costs, and physical space requirements.</p>
</li>
<li><p><strong>Deployment complexity:</strong> Setting up a new server, installing the OS, and configuring applications was a time-consuming process.</p>
</li>
<li><p><strong>Resource conflicts:</strong> Different applications on the same physical server could have conflicting software dependencies, leading to instability.</p>
</li>
<li><p><strong>Disaster recovery challenges:</strong> Recovering a failed physical server and its applications could be a lengthy and complex ordeal.</p>
</li>
</ul>
<p><strong>Virtualisation directly addresses these issues by</strong>:</p>
<ul>
<li><p><strong>Maximising hardware utilisation:</strong> Multiple VMs can share the same physical hardware, significantly increasing the return on investment for server infrastructure.</p>
</li>
<li><p><strong>Reducing physical infrastructure:</strong> Fewer physical servers mean lower power consumption, reduced cooling costs, and a smaller data center footprint.</p>
</li>
<li><p><strong>Faster provisioning:</strong> VMs can be quickly cloned, deployed from templates, or spun up on demand, drastically reducing deployment times.</p>
</li>
<li><p><strong>Enhanced isolation:</strong> Each VM operates in its own isolated environment, preventing conflicts between applications and ensuring stability.</p>
</li>
<li><p><strong>Improved disaster recovery:</strong> VMs can be easily backed up, replicated, and restored, leading to quicker recovery times in the event of hardware failure or data loss.</p>
</li>
</ul>
<hr />
<h3><strong>Containerisation: The Lightweight Revolution</strong></h3>
<p><strong>What it is</strong>:<br />Containerisation is a more <strong>lightweight</strong> form of virtualisation that packages an application and all its dependencies (code, runtime, libraries, configuration files) into a single, self-contained unit called a “container.” Unlike VMs, containers <strong>do not</strong> include a full operating system. Instead, they share the host operating system’s kernel.</p>
<p>A “container engine” (like Docker) manages these containers, providing the necessary isolation and resource management. Think of it as a highly efficient, portable, and isolated environment specifically for running applications.</p>
<p><strong>Problems it solves:</strong></p>
<p>While virtualisation solved many problems, new challenges emerged, particularly in the realm of application development and deployment:</p>
<ul>
<li><p><strong>“It works on my machine” syndrome:</strong> Developers often faced issues where applications ran perfectly on their local development environment but failed in testing or production environments due to subtle differences in underlying configurations or dependencies.</p>
</li>
<li><p><strong>Slower deployment and startup:</strong> Even with VMs, booting a full operating system for each application could be time-consuming.</p>
</li>
<li><p><strong>Resource overhead for microservices:</strong> As applications became more modular (microservices), spinning up a separate VM for each small service was resource-intensive and inefficient.</p>
</li>
<li><p><strong>Portability across environments:</strong> Ensuring an application behaved consistently across different cloud providers or on-premises infrastructure could be challenging.</p>
</li>
</ul>
<hr />
<h3><strong>Containerisation tackles these problems head-on:</strong></h3>
<ul>
<li><p><strong>Environmental consistency:</strong> By bundling all dependencies, containers ensure that an application runs consistently from development to testing to production, eliminating environment-related bugs.</p>
</li>
<li><p><strong>Rapid deployment and startup:</strong> Containers start up in seconds (or even milliseconds) because they don’t need to boot an entire OS, enabling faster development cycles and quicker scaling.</p>
</li>
<li><p><strong>Lightweight and efficient:</strong> Sharing the host kernel means containers consume significantly fewer resources than VMs, allowing for higher density of applications on a single server.</p>
</li>
<li><p><strong>Unparalleled portability:</strong> A container can run on any system that has a compatible container engine, regardless of the underlying infrastructure, making it ideal for hybrid and multi-cloud strategies.</p>
</li>
<li><p><strong>Simplified dependency management:</strong> All application dependencies are packaged within the container, simplifying installation and preventing conflicts with other applications on the host system.</p>
</li>
</ul>
<hr />
<p><strong>Key Differences: A Side-by-Side Comparison:</strong></p>
<p>While both technologies abstract resources, their approach and scope differ fundamentally:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772367674/9061491d-03da-4e34-84f3-f6b5bb868400.png" alt="" />

<hr />
<h3><strong>Conclusion</strong></h3>
<p>Neither virtualisation nor containerisation is inherently “better” than the other. They are powerful tools that solve different problems and excel in different scenarios.<br /><em><strong>Virtualisation</strong></em> provides robust isolation and the flexibility to run diverse operating systems, making it a cornerstone of cloud computing and traditional server consolidation.<br /><em><strong>Containerisation</strong></em>, with its lightweight nature and rapid deployment capabilities, has become the de facto standard for modern, agile application development and microservices architectures.</p>
<p>Understanding their distinct characteristics allow us to choose the right tool for the job, building efficient, scalable, and resilient IT infrastructure in our homelabs or business.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding ISO 27001: Building a Modern Information Security Management System]]></title><description><![CDATA[Information security is no longer limited to firewalls, antivirus software, or password policies. Modern organisations operate in environments where cyber threats, ransomware attacks, insider risks, s]]></description><link>https://blog.tech-journey.co.za/iso-27001-an-essential-guide-to-information-security</link><guid isPermaLink="true">https://blog.tech-journey.co.za/iso-27001-an-essential-guide-to-information-security</guid><category><![CDATA[ISO 27001]]></category><category><![CDATA[compliance ]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Fri, 23 May 2025 10:42:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772355024/e3c0a244-b2b5-4e8f-9f77-1c85126e8972.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Information security is no longer limited to firewalls, antivirus software, or password policies. Modern organisations operate in environments where cyber threats, ransomware attacks, insider risks, supply chain vulnerabilities, and regulatory pressures are constant operational concerns. A single security incident can result in financial loss, reputational damage, legal consequences, and prolonged service disruption.</p>
<p>As organisations become increasingly dependent on digital systems, cloud infrastructure, remote work, and interconnected services, information security must evolve from a purely technical function into a structured business discipline. This is where ISO 27001 plays a critical role.</p>
<p>ISO 27001 is an internationally recognised standard for Information Security Management Systems (ISMS). It provides organisations with a structured framework for identifying, managing, and reducing information security risks while ensuring the confidentiality, integrity, and availability of critical information assets.</p>
<p>Rather than focusing on individual technologies or isolated security controls, ISO 27001 takes a holistic approach to information security governance. It combines people, processes, policies, risk management, and technical controls into a single management system designed to support continual improvement and long-term operational resilience.</p>
<hr />
<h3>What is ISO 27001?</h3>
<p>ISO 27001 forms part of the <a href="https://www.iso.org/standard/iso-iec-27000-family">ISO/IEC 27000 family of standards</a>, which focuses on information security management and governance. The standard defines the requirements for establishing, implementing, maintaining, and continually improving an Information Security Management System.</p>
<p>At its core, ISO 27001 helps organisations build repeatable and measurable security practices rather than relying on reactive or ad hoc approaches to cybersecurity.</p>
<p>The framework is designed to help organisations:</p>
<ul>
<li><p>Identify information security risks</p>
</li>
<li><p>Assess vulnerabilities and threats</p>
</li>
<li><p>Implement appropriate security controls</p>
</li>
<li><p>Monitor control effectiveness</p>
</li>
<li><p>Improve security governance over time</p>
</li>
<li><p>Demonstrate compliance and accountability</p>
</li>
</ul>
<p>ISO 27001 is intentionally flexible and can be implemented across organisations of different sizes, industries, and technical environments, from small businesses to multinational enterprises.</p>
<hr />
<h3>The Information Security Management System (ISMS)</h3>
<p>The foundation of ISO 27001 is the Information Security Management System, commonly referred to as the ISMS.</p>
<p>An ISMS is not simply a collection of security tools or technical configurations. It is a structured management framework that governs how an organisation protects its information assets and manages security risks.</p>
<p>The ISMS brings together:</p>
<ul>
<li><p>Policies and procedures</p>
</li>
<li><p>Governance structures</p>
</li>
<li><p>Technical controls</p>
</li>
<li><p>Operational processes</p>
</li>
<li><p>Employee responsibilities</p>
</li>
<li><p>Risk management activities</p>
</li>
<li><p>Monitoring and auditing mechanisms</p>
</li>
</ul>
<p>This ensures that information security becomes embedded within the organisation rather than treated as a standalone IT responsibility.</p>
<p>A well-implemented ISMS enables organisations to manage security consistently across departments, systems, suppliers, and operational environments.</p>
<hr />
<h3>The Core Principles of ISO 27001</h3>
<p>ISO 27001 is built around several fundamental principles that shape how organisations approach information security.</p>
<h3>Confidentiality</h3>
<p>Confidentiality ensures that sensitive information is only accessible to authorised individuals, systems, or processes. This includes protecting customer data, intellectual property, financial records, employee information, and operational systems from unauthorised disclosure.</p>
<p>Controls supporting confidentiality may include:</p>
<ul>
<li><p>Access control policies</p>
</li>
<li><p>MFA</p>
</li>
<li><p>Encryption</p>
</li>
<li><p>Privileged access management</p>
</li>
<li><p>Network segmentation</p>
</li>
</ul>
<hr />
<h3>Integrity</h3>
<p>Integrity focuses on maintaining the accuracy, consistency, and trustworthiness of information throughout its lifecycle.</p>
<p>Organisations must ensure that information cannot be altered, deleted, or manipulated without authorisation. This is especially important for financial systems, databases, operational records, and compliance-related information.</p>
<p>Controls supporting integrity often include:</p>
<ul>
<li><p>Hashing and integrity verification</p>
</li>
<li><p>Change management procedures</p>
</li>
<li><p>Audit logging</p>
</li>
<li><p>Version control</p>
</li>
<li><p>Database protection mechanisms</p>
</li>
</ul>
<hr />
<h3>Availability</h3>
<p>Availability ensures that information and systems remain accessible when required by authorised users.</p>
<p>Service outages, ransomware attacks, infrastructure failures, and denial-of-service incidents can severely impact business operations. ISO 27001 encourages organisations to build resilience into their systems and operational processes.</p>
<p>Availability-related controls commonly include:</p>
<ul>
<li><p>Backups and disaster recovery</p>
</li>
<li><p>Redundancy and failover systems</p>
</li>
<li><p>Business continuity planning</p>
</li>
<li><p>Monitoring and alerting</p>
</li>
<li><p>Incident response procedures</p>
</li>
</ul>
<hr />
<h3>Risk Assessment and Risk Treatment</h3>
<p>Risk management sits at the centre of ISO 27001.</p>
<p>Rather than applying security controls blindly, organisations are expected to identify and evaluate risks based on their specific business environment, threat landscape, and operational requirements.</p>
<p>This process typically includes several stages:</p>
<h3>Risk Identification</h3>
<p>Organisations identify threats, vulnerabilities, assets, and operational weaknesses that could impact information security.</p>
<p>Examples include:</p>
<ul>
<li><p>Phishing attacks</p>
</li>
<li><p>Weak access controls</p>
</li>
<li><p>Outdated software</p>
</li>
<li><p>Insider threats</p>
</li>
<li><p>Insecure third-party suppliers</p>
</li>
<li><p>Misconfigured cloud services</p>
</li>
</ul>
<hr />
<h3>Risk Analysis</h3>
<p>Once risks are identified, organisations assess:</p>
<ul>
<li><p>The likelihood of the threat occurring</p>
</li>
<li><p>The potential business impact</p>
</li>
<li><p>Affected systems or services</p>
</li>
<li><p>Operational consequences</p>
</li>
<li><p>Financial and reputational exposure</p>
</li>
</ul>
<p>This helps organisations understand which risks require immediate attention.</p>
<hr />
<h3>Risk Evaluation</h3>
<p>Not all risks carry the same level of severity. ISO 27001 requires organisations to prioritise risks according to defined risk criteria and business objectives.</p>
<p>This enables leadership teams to allocate resources effectively and focus on the most significant security concerns first.</p>
<hr />
<h3>Risk Treatment</h3>
<p>After risks are evaluated, organisations implement appropriate controls to reduce, transfer, avoid, or accept identified risks.</p>
<p>Examples of treatment strategies may include:</p>
<ul>
<li><p>Implementing security technologies</p>
</li>
<li><p>Strengthening governance policies</p>
</li>
<li><p>Improving employee awareness training</p>
</li>
<li><p>Introducing monitoring solutions</p>
</li>
<li><p>Reducing unnecessary system exposure</p>
</li>
<li><p>Enforcing stricter access management</p>
</li>
</ul>
<p>This risk-based approach is one of the reasons ISO 27001 remains widely respected across industries.</p>
<hr />
<h3>Security Controls and Annex A</h3>
<p>ISO 27001 includes a comprehensive set of reference controls designed to support information security risk management. These controls are outlined within Annex A of the standard.</p>
<p>The controls cover multiple security domains, including:</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Access Control</mark></strong>: Ensuring users only have access to systems and information required for their roles.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Asset Management</mark></strong>: Identifying, classifying, and protecting organisational information assets throughout their lifecycle.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Cryptography</mark></strong>: Using encryption and cryptographic controls to protect sensitive data.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Incident Management</mark></strong>: Establishing formal procedures for detecting, reporting, responding to, and recovering from security incidents.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Supplier Security</mark></strong>: Managing risks associated with third-party vendors, service providers, and supply chain relationships.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Business Continuity</mark></strong>: Ensuring critical operations can continue during disruptive events or security incidents.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Compliance</mark></strong>: Meeting legal, regulatory, contractual, and industry-specific security obligations.</p>
<p>Importantly, ISO 27001 does not force organisations to implement every control exactly as written. Instead, controls are selected based on identified risks, business requirements, and operational context.</p>
<p>This flexibility allows organisations to tailor their ISMS appropriately while still maintaining compliance with the standard.</p>
<hr />
<h3>Continual Improvement and Security Maturity</h3>
<p>One of the most important aspects of ISO 27001 is its emphasis on continual improvement.</p>
<p>Cybersecurity is not static. Threat actors evolve, technologies change, business operations expand, and new vulnerabilities emerge constantly. Controls that were effective a year ago may no longer provide adequate protection today.</p>
<p>ISO 27001 encourages organisations to continuously:</p>
<ul>
<li><p>Review security risks</p>
</li>
<li><p>Assess control effectiveness</p>
</li>
<li><p>Analyse incidents and audit findings</p>
</li>
<li><p>Update policies and procedures</p>
</li>
<li><p>Improve employee awareness</p>
</li>
<li><p>Strengthen operational resilience</p>
</li>
</ul>
<p>This ongoing improvement cycle helps organisations develop stronger security maturity over time rather than treating security as a one-time project.</p>
<hr />
<h3>Implementing ISO 27001</h3>
<p>Implementing ISO 27001 is typically a long-term operational initiative rather than a quick technical deployment.</p>
<p>Successful implementation usually involves several stages.</p>
<h3>Defining the Scope</h3>
<p>Organisations first determine which systems, departments, locations, services, or business units will fall within the ISMS scope.</p>
<p>A clearly defined scope is essential for effective governance and audit preparation.</p>
<hr />
<h3>Conducting a Gap Assessment</h3>
<p>Many organisations begin by evaluating their current security posture against ISO 27001 requirements.</p>
<p>This helps identify:</p>
<ul>
<li><p>Missing policies</p>
</li>
<li><p>Weak controls</p>
</li>
<li><p>Documentation gaps</p>
</li>
<li><p>Governance weaknesses</p>
</li>
<li><p>Operational risks</p>
</li>
</ul>
<hr />
<h3>Developing Policies and Procedures</h3>
<p>Organisations establish formal security policies, standards, procedures, and operational guidelines that support the ISMS.</p>
<p>These documents often cover:</p>
<ul>
<li><p>Access management</p>
</li>
<li><p>Incident response</p>
</li>
<li><p>Acceptable use</p>
</li>
<li><p>Data protection</p>
</li>
<li><p>Supplier management</p>
</li>
<li><p>Business continuity</p>
</li>
<li><p>Change management</p>
</li>
</ul>
<hr />
<h3>Implementing Security Controls</h3>
<p>Technical and administrative controls are then implemented based on identified risks and business requirements.</p>
<p>This may include:</p>
<ul>
<li><p>Endpoint protection</p>
</li>
<li><p>SIEM platforms</p>
</li>
<li><p>Vulnerability management</p>
</li>
<li><p>MFA enforcement</p>
</li>
<li><p>Backup solutions</p>
</li>
<li><p>Monitoring and logging</p>
</li>
<li><p>Network security controls</p>
</li>
</ul>
<hr />
<h3>Training and Security Awareness</h3>
<p>Employees play a significant role in organisational security.</p>
<p>ISO 27001 requires organisations to ensure staff understand:</p>
<ul>
<li><p>Security responsibilities</p>
</li>
<li><p>Acceptable behaviour</p>
</li>
<li><p>Reporting procedures</p>
</li>
<li><p>Phishing awareness</p>
</li>
<li><p>Data handling requirements</p>
</li>
</ul>
<p>Human error remains one of the most common causes of security incidents, making awareness training a critical component of the ISMS.</p>
<hr />
<h3>Internal Auditing and Management Reviews</h3>
<p>Internal audits help organisations assess whether controls are operating effectively and whether the ISMS remains compliant with ISO 27001 requirements.</p>
<p>Management reviews ensure leadership maintains visibility into:</p>
<ul>
<li><p>Risks</p>
</li>
<li><p>Incidents</p>
</li>
<li><p>Audit findings</p>
</li>
<li><p>Compliance status</p>
</li>
<li><p>Improvement opportunities</p>
</li>
</ul>
<p>Strong executive involvement is essential for long-term success.</p>
<hr />
<h3>ISO 27001 Certification</h3>
<p>Organisations can pursue certification through accredited external auditors who assess whether the ISMS complies with ISO 27001 requirements.</p>
<p>Certification demonstrates that an organisation has implemented a structured and auditable information security management framework.</p>
<p>However, certification should not be mistaken for guaranteed security.</p>
<p>A certified organisation can still experience security incidents. The value of ISO 27001 lies in establishing mature governance, repeatable security processes, accountability, and continual improvement mechanisms that strengthen overall resilience.</p>
<p>Maintaining certification also requires ongoing audits, evidence collection, monitoring, and operational commitment.</p>
<hr />
<h3>Benefits of ISO 27001</h3>
<p>Organisations implementing ISO 27001 often gain several operational and strategic advantages.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Improved Information Security</mark></strong>: A structured security framework helps reduce vulnerabilities, strengthen controls, and improve risk visibility across the organisation.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Stronger Regulatory Compliance</mark></strong>: ISO 27001 supports compliance with data protection laws, contractual obligations, and industry-specific regulatory requirements.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Increased Customer and Stakeholder Trust</mark></strong>: Certification demonstrates that the organisation takes information security seriously and follows internationally recognised security practices.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Better Operational Resilience</mark></strong>: Improved incident management, business continuity planning, and governance processes help organisations recover more effectively from disruptions.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Enhanced Risk Management</mark></strong>: The risk-based approach enables organisations to make informed security decisions aligned with business priorities.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Competitive Advantage</mark></strong>: Many organisations now require suppliers and service providers to demonstrate formal security governance. ISO 27001 certification can strengthen commercial credibility and improve market positioning.</p>
<hr />
<h3>Final Thoughts</h3>
<p>ISO 27001 has become one of the most widely recognised standards for information security management because it addresses security as a business-wide operational discipline rather than purely a technical problem.</p>
<p>Its strength lies in combining governance, risk management, security controls, continual improvement, and organisational accountability into a structured framework that can evolve alongside the business.</p>
<p>In a world where cyber threats continue to grow in sophistication and impact, organisations need more than isolated security tools or reactive incident handling. They need mature, repeatable, and measurable security practices that support long-term resilience.</p>
<p>ISO 27001 provides the foundation for building exactly that.</p>
]]></content:encoded></item><item><title><![CDATA[Introducing New Technologies and Managing Dependencies]]></title><description><![CDATA[New technologies show up constantly. New frameworks, libraries, observability tools, deployment platforms, security scanners. Most of them are genuinely useful in the right context. Some are redundant]]></description><link>https://blog.tech-journey.co.za/introducing-new-technologies-and-managing-dependencies</link><guid isPermaLink="true">https://blog.tech-journey.co.za/introducing-new-technologies-and-managing-dependencies</guid><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sun, 13 Apr 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/a614fbf0-52da-4247-b75a-ade33501af4f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>New technologies show up constantly. New frameworks, libraries, observability tools, deployment platforms, security scanners. Most of them are genuinely useful in the right context. Some are redundant. A few quietly introduce long-term operational pain that only becomes visible months later when systems start to scale or fail under pressure.</p>
<p>The hard part isn’t finding tools but deciding <strong>what <em>deserves to exist in your stack</em></strong>.</p>
<p>That decision has more consequences than it initially appears to. Every addition increases complexity somewhere: <strong>upgrades</strong>, <strong>security exposure</strong>, <strong>debugging surface area</strong>, <strong>onboarding overhead</strong>, <strong>deployment risk</strong>.</p>
<p>Even “<em>simple</em>” dependencies tend to accumulate behaviour you didn’t explicitly design for.</p>
<p>So the question isn’t “<strong>is this good technology?</strong>” It’s “<strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">is this worth embedding into our system?</mark></strong>”</p>
<hr />
<h3>Start With the Problem, <mark class="bg-yellow-200 dark:bg-yellow-500/30">Not</mark> the Tool</h3>
<p>A common failure mode is starting with the technology and then looking for where to use it. This often ends with unnecessary abstraction.</p>
<p>A more reliable approach is simpler: <strong>define the problem in operational terms <mark class="bg-yellow-200 dark:bg-yellow-500/30">first</mark></strong>.</p>
<p>Not:</p>
<blockquote>
<p>“We should use Kafka”</p>
</blockquote>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">But</mark></strong>:</p>
<blockquote>
<p>“We need a reliable way to process events when downstream services are intermittently unavailable”</p>
</blockquote>
<p>Once the problem is precise, the set of valid solutions becomes much smaller. Sometimes the answer is still Kafka. Sometimes it’s not even a queue.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">If the problem can already be solved with what exists in the system, introducing something new needs a stronger justification than preference or familiarity.</mark></strong></p>
<hr />
<h3>Do the Research <mark class="bg-yellow-200 dark:bg-yellow-500/30">Properly</mark> (Not Superficially)</h3>
<p>Research isn’t reading a homepage and checking GitHub stars.</p>
<p>A proper evaluation usually includes a few uncomfortable questions:</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Who maintains this, and how stable is it really?</mark></strong> Some projects look healthy but are effectively driven by one or two contributors. That’s fine until it isn’t. A sudden slowdown in maintenance becomes <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">your</mark></strong> problem when you’re mid-production rollout.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">How does it fail? </mark></strong> Most tools work well when everything is ideal. Fewer behave predictably under load, partial outages, misconfigurations, or version drift.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What is the upgrade story?</mark></strong> This is often ignored. A tool isn’t just what it is today but what it becomes over three years of updates, migrations, and breaking changes.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Where does it sit in the ecosystem? </mark></strong> A dependency that integrates cleanly into your stack is very different from one that forces architectural changes just to function properly.</p>
<p>At this stage, the goal isn’t to prove the tool works but merely finding reasons to <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">not</mark></strong> use it.</p>
<hr />
<h3><mark class="bg-yellow-200 dark:bg-yellow-500/30">Dependencies Are Not Free</mark> (Even When They Feel Like It)</h3>
<p>Every dependency introduces long-term commitments:</p>
<ul>
<li><p>Security updates you didn’t write</p>
</li>
<li><p>Behavioural changes you don’t fully control</p>
</li>
<li><p>Transitive dependencies (dependencies of dependencies)</p>
</li>
<li><p>Version conflicts across the stack</p>
</li>
<li><p>Hidden performance overhead</p>
</li>
</ul>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">That last one is often underestimated: </mark></strong> A small library can bring in a large chain of packages that quietly increase memory usage, bundle size, or startup time.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">A useful discipline is this:</mark></strong> if you can reasonably implement and maintain something internally without increasing system risk, that option should at least be considered.</p>
<p>Not always chosen. But considered.</p>
<hr />
<h3>Keep Dependency Graphs Small and Understandable</h3>
<p>There’s a difference between using dependencies and relying on them blindly.</p>
<p>A healthy system has:</p>
<ul>
<li><p>A small number of critical dependencies</p>
</li>
<li><p>Clear reasoning for each one</p>
</li>
<li><p>Visibility into what each dependency brings in transitively</p>
</li>
</ul>
<p>An unhealthy system tends to grow in layers: <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">framework</mark></strong> → <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">plugin</mark></strong> → <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">utility library</mark></strong> → <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">helper package</mark></strong> → <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">another abstraction layer</mark></strong></p>
<p>At some point, you’re not really building software anymore. You’re assembling someone else’s architecture and <em><strong>hoping</strong></em> it behaves predictably.</p>
<hr />
<h3>Testing New Technology</h3>
<p>The biggest mistake is treating adoption as a coding exercise and not a systems test.</p>
<h3>Start with a small, honest PoC</h3>
<p>A PoC should answer only one question: <mark class="bg-yellow-200 dark:bg-yellow-500/30"> does this behave as expected in </mark> <em><mark class="bg-yellow-200 dark:bg-yellow-500/30">your environment</mark></em><mark class="bg-yellow-200 dark:bg-yellow-500/30">?</mark></p>
<p>Not in a tutorial or demo but in your infrastructure, with your constraints, your traffic patterns, your failure modes.</p>
<p><strong>If it only works in <em>ideal</em> conditions, it’s not validated</strong>.</p>
<hr />
<h3>Break it on purpose</h3>
<p>Most tools are easy to evaluate when everything is stable. The real signal comes from degradation testing:</p>
<ul>
<li><p><strong>What happens under load spikes?</strong></p>
</li>
<li><p><strong>What happens when a dependency becomes slow?</strong></p>
</li>
<li><p><strong>What happens when configuration is slightly wrong?</strong></p>
</li>
<li><p><strong>What happens when one component fails silently?</strong></p>
</li>
</ul>
<p>This is where differences between “works” and “reliable” become obvious.</p>
<hr />
<h3>Test integration, not isolation</h3>
<p>A tool that works alone is not enough.</p>
<p>You want to see:</p>
<ul>
<li><p>How it interacts with authentication systems</p>
</li>
<li><p>How it behaves in CI/CD pipelines</p>
</li>
<li><p>How it impacts logging and observability</p>
</li>
<li><p>How it affects deployment workflows</p>
</li>
<li><p>How it fits into incident response processes</p>
</li>
</ul>
<p><strong>Integration problems are usually the ones that surface late and cost the most time.</strong></p>
<hr />
<h3>Rollout Should <mark class="bg-yellow-200 dark:bg-yellow-500/30">Never</mark> Be Immediate</h3>
<p>Even when everything looks good, full deployment is rarely the right first step.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Safer patterns</mark></strong> exist for a reason.</p>
<p>A <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">canary release</mark></strong> exposes the change to a small subset of traffic first. If something breaks, impact stays contained.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Blue-green</mark></strong> deployments keep two environments running so you can switch quickly if needed.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Feature flags</mark></strong> allow behaviour changes without redeploying code.</p>
<p>These aren’t “advanced techniques” but basic safety mechanisms for real systems.</p>
<hr />
<h3>Don’t Ignore What Happens After Adoption</h3>
<p>The work doesn’t end once the tool is in production.</p>
<p>In fact, that’s where the real evaluation begins.</p>
<p>You <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">need</mark></strong> visibility into:</p>
<ul>
<li><p>Performance changes over time</p>
</li>
<li><p>Error rates and failure patterns</p>
</li>
<li><p>Resource consumption trends</p>
</li>
<li><p>Unexpected interactions with other services</p>
</li>
</ul>
<p>This is where <em><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">observability</mark></strong></em> comes in. <strong>Logs</strong>, <strong>metrics</strong>, and <strong>traces</strong> gives you insight into what your system is doing.</p>
<p>Without that, every future incident becomes guesswork.</p>
<hr />
<h3>Importance of Documentation</h3>
<p>If a technology is introduced but not clearly documented, it effectively doesn’t exist outside the original implementers’ heads.</p>
<p><strong>At minimum</strong>, you need clarity on:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Why it was introduced</mark></strong></p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What problem it solved</mark></strong></p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">How it should be operated</mark></strong></p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">What “normal behaviour” looks like</mark></strong></p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">How to roll it back</mark></strong></p>
</li>
</ul>
<p>Most operational failures are caused by missing context and not the tools.</p>
<hr />
<h3>Decision Flow for Implementation</h3>
<p>A simple structure tends to hold up better than complex frameworks:</p>
<ol>
<li><p><strong>Define the problem clearly</strong></p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Check existing tools first</mark></strong></p>
</li>
<li><p><strong>Evaluate a small set of candidates</strong></p>
</li>
<li><p><strong>Validate assumptions with a PoC</strong></p>
</li>
<li><p><strong>Test failure scenarios, not just success cases</strong></p>
</li>
<li><p><strong>Integrate into a controlled environment</strong></p>
</li>
<li><p><strong>Roll out gradually</strong></p>
</li>
<li><p><strong>Observe real-world behaviour</strong></p>
</li>
<li><p><strong>Decide whether to keep, replace, or remove</strong></p>
</li>
</ol>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Importantly</mark></strong>: removal should always remain an acceptable outcome. Not every experiment deserves to become permanent infrastructure.</p>
<hr />
<h3><strong>Key Takeaways</strong></h3>
<p>Most technical systems don’t fail because teams choose “bad” technology but because too many reasonable decisions accumulate without enough scrutiny.</p>
<p>The goal is simply to avoid <em><strong>uncontrolled</strong></em> growth.</p>
<p>Good engineering practice is less about choosing the perfect stack and more about maintaining the ability to <strong>understand</strong>, <strong>replace</strong>, and <strong>reason</strong> about what <strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">already exists</mark></strong>.</p>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">If a system stays understandable, it stays maintainable. Everything else is secondary.</mark></strong></p>
]]></content:encoded></item><item><title><![CDATA[From Overthinking to Execution: Confronting Analysis Paralysis in Tech]]></title><description><![CDATA[Analysis Paralysis
If you’ve ever gone down a rabbit hole trying to find the “perfect” resolution for a challenging ticket, rewritten your troubleshooting notes five times just to make it sound right,]]></description><link>https://blog.tech-journey.co.za/analysis-paralysis-in-tech</link><guid isPermaLink="true">https://blog.tech-journey.co.za/analysis-paralysis-in-tech</guid><category><![CDATA[decision making]]></category><category><![CDATA[analysis paralysis]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Fri, 11 Apr 2025 17:46:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772349781/0390fdc6-953f-426b-b3b3-d84a6bb47cf2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Analysis Paralysis</h2>
<p>If you’ve ever gone down a rabbit hole trying to find the “perfect” resolution for a challenging ticket, rewritten your troubleshooting notes five times just to make it sound right, or delayed starting a certification because you didn’t feel 100% ready, you’re not the only one. Most of us in IT have been there. It’s not laziness or lack of discipline. It’s analysis paralysis which is a common experience among professionals in all areas of IT.</p>
<p>This article explores how overthinking can subtly stall <strong>career growth</strong>, <strong>personal development</strong>, and <strong>technical confidence</strong>. More importantly, it offers practical strategies I’m using to break through it.</p>
<h3>What Is Analysis Paralysis?</h3>
<p>Analysis paralysis is a state of inaction caused by over-analysing choices, potential outcomes, or the fear of making the wrong decision. It’s often mistaken for diligence or caution, but in reality, it <strong>undermines progress</strong>.</p>
<p>In IT, it typically shows up as:</p>
<ul>
<li><p>Constantly switching between tools, methods, or documentation without actually resolving the issue.</p>
</li>
<li><p>Obsessing over the “perfect” solution instead of implementing a working fix.</p>
</li>
<li><p>Consuming endless content but producing no tangible results.</p>
</li>
</ul>
<p>Over time, this leads to frustration, imposter syndrome, and burnout. Not from working too hard, but from overthinking for too long.</p>
<h3>Why It’s Common in IT Roles</h3>
<p>IT professionals, especially those who are detail-oriented, introverted, and/or self-taught are particularly vulnerable to this trap. We’re trained to anticipate failure scenarios, weigh up options, and avoid misconfiguration. However, that mindset, when applied to every single task, can become counterproductive.</p>
<p><strong>Contributing Factors:</strong></p>
<ul>
<li><p><strong>Perfectionism:</strong> The belief that only flawless work is worth sharing or implementing.</p>
</li>
<li><p><strong>Fear of failure:</strong> Especially in front of a user or a manager.</p>
</li>
<li><p><strong>Tool overload:</strong> The paralysis that comes from having too many options for a given task.</p>
</li>
<li><p><strong>Imposter syndrome:</strong> Feeling the need to “know more” before helping a user or starting a project.</p>
</li>
</ul>
<p>When these factors combine, even small decisions such as choosing the best approach to a ticket or deciding on a new piece of software to test  can feel overwhelming especially when it impacts the business.</p>
<h3>How I’m Working Through It</h3>
<p>I haven’t completely overcome this, but I’ve developed practical strategies coupled with mindfulness that help me take consistent action while staying technically disciplined. The working methods listed below is what I‘ve been using daily in my work and personal projects.</p>
<ol>
<li><p><strong>Prioritise Delivery Over Perfection</strong>: Rather than waiting until something is “perfectly documented” or “fully automated,” I commit to delivering a first iteration. A working solution however basic , creates momentum, builds confidence, and uncovers genuine problems worth solving.</p>
</li>
<li><p><strong>Impose Intentional Constraints:</strong> Rather than keeping all options open, an approach that often leads to decision fatigue ,  I intentionally narrow my choices. For each project, I commit to a single scripting language, one troubleshooting methodology, or one specific tool. This practice prevents me from falling into the “<a href="https://thetreasureswithin.net/shiny-object-syndrome/#:~:text=new%20shiny%20idea.-,What%20Is%20Shiny%20Object%20Syndrome?,for%20new%20trends%20to%20follow.">shiny object syndrome</a>” trap, where chasing new technologies distracts from execution. Clear constraints reduce cognitive overhead and force deeper focus, leading to better outcomes and a more disciplined workflow.</p>
</li>
<li><p><strong>Document While You Learn :</strong> Creating documentation  whether it be a ticketing system, a team wiki, or a personal notebook  forces structure into learning. It transforms passive reading into active knowledge-building. If I can explain a concept or a solution clearly, I understand it well enough to act on it.</p>
</li>
<li><p><strong>Set External Accountability:</strong> Self-imposed deadlines are easy to ignore. External ones are harder. I’ve started sharing ticket progress or project updates with colleagues. It creates a gentle pressure to follow through.</p>
</li>
<li><p><strong>Normalise Imperfect Output:</strong> It’s tempting to compare ourselves to polished help guides or perfectly organised repositories. But what actually earns respect in the industry is consistently turning up, visibly solving problems, and documenting the journey.</p>
</li>
</ol>
<h3>The Core Skill: Comfortable Incompletion</h3>
<p>In an environment saturated with tools, knowledge bases, and endless updates, the most valuable skill is the ability to act without <strong>knowing everything</strong>(which is impossible anyway).</p>
<p>Progress usually comes from execution under uncertainty.<br />In other words: <strong>start first. Refine later.</strong></p>
<h3>A Challenge, If You’re Currently Stuck</h3>
<p>If you’ve been delaying a project, ticket or are stuck in research mode, try this:</p>
<ul>
<li><p>Choose one task you’ve postponed.</p>
</li>
<li><p>Ask yourself: <strong>“Realistically, how long would this take me if I focused on just getting it done?”</strong> This simple question helps cut through overthinking and gives you a rough timebox to work within. It brings clarity to what might otherwise feel vague or overwhelming.</p>
</li>
<li><p>Use <a href="https://pomofocus.io/app"><strong>Pomofocus</strong></a> to help you get the ball rolling.</p>
</li>
<li><p><strong>DON’T</strong> be afraid to ask for help. 🙂</p>
</li>
<li><p>Write a short summary of what you learned or struggled with.</p>
</li>
</ul>
<p>Do this not to impress anyone, but to rewire the habit.</p>
<h3>Conclusion</h3>
<p>Overthinking is a symptom of caring deeply about doing things right. This same drive, when channeled into action, becomes a powerful strength.</p>
<p>To unlock this strength, we must shift our focus from seeking perfection to embracing progress. We can achieve this by <strong>designing, documenting, testing, and iterating</strong>, even when things aren’t perfect. By <strong>normalising building before we feel completely ready</strong>, we can transform our tendency to overthink into a powerful force for creation and innovation.</p>
]]></content:encoded></item><item><title><![CDATA[Automate System Monitoring & Alerting with Uptime Kuma and Freshdesk]]></title><description><![CDATA[This guide covers setting up Uptime Kuma to monitor a system, trigger email alerts to Freshdesk on downtime, and automatically create tickets with the correct priority and assignment.



This setup sh]]></description><link>https://blog.tech-journey.co.za/automate-system-monitoring-alerting-with-uptime-kuma-and-freshdesk</link><guid isPermaLink="true">https://blog.tech-journey.co.za/automate-system-monitoring-alerting-with-uptime-kuma-and-freshdesk</guid><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Tue, 04 Mar 2025 15:35:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772463646/5f78df6d-b44f-4384-a0cf-ceca0a854e12.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This guide covers setting up Uptime Kuma to monitor a system, trigger email alerts to Freshdesk on downtime, and automatically create tickets with the correct priority and assignment.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772446909/ef937fce-6a41-41b1-ad95-f031263c3b88.gif" alt="" />

<blockquote>
<p>This setup showcases a real-world integration that streamlines incident management by ensuring downtime alerts are promptly turned into actionable tickets, reducing manual intervention and improving response times.</p>
</blockquote>
<h3>Prerequisites</h3>
<ul>
<li><p>Before we begin, ensure you have the following:</p>
</li>
<li><p><a href="https://docs.docker.com/engine/install/ubuntu/"><strong>Docker</strong></a> <strong>&amp;</strong> <a href="https://docs.docker.com/compose/install/linux/"><strong>Docker Compose</strong></a> installed on an <strong>Ubuntu 22.04 server</strong>.</p>
</li>
<li><p>A <strong>Freshdesk account</strong> (<a href="https://www.freshworks.com/freshdesk/lp/home/?tactic_id=6780391&amp;utm_source=google-adwords&amp;utm_medium=FD-Search-Brand-Broad-MEA-Tier-2&amp;utm_campaign=FD-Search-Brand-Broad-MEA-Tier-2&amp;utm_term=freshdesk%20plans&amp;device=c&amp;matchtype=e&amp;network=g&amp;gclid=CjwKCAiAw5W-BhAhEiwApv4goJ0heygRCG1iS9pt0HGjg7lLmOnCU9dvi_M7qGcBRFsHFMnMgY9KBhoCVfsQAvD_BwE&amp;audience=kwd-47765166336&amp;ad_id=718584053892&amp;gad_source=1">Free plan</a>  - simply  scroll to the bottom of their website to sign up).</p>
</li>
<li><p>A <strong>custom Freshdesk email address</strong> for ticket creation (e.g., <code>support@yourcompany.freshdesk.com</code>).</p>
</li>
<li><p><strong>SMTP credentials</strong> for sending emails from <strong>Uptime Kuma</strong>.</p>
</li>
</ul>
<hr />
<h3>What is Uptime Kuma?</h3>
<p>A self-hosted monitoring tool that allows you to track the uptime and performance of your <strong>websites, APIs, and services</strong>.</p>
<h3>Key Features:</h3>
<ul>
<li><p><strong>Supports multiple monitoring options</strong>: <strong>HTTP, HTTPS, TCP, ICMP Ping, DNS</strong>, and more.</p>
</li>
<li><p><strong>Customisable alerts</strong>:</p>
</li>
<li><p><strong>Email, webhooks, Slack, Discord</strong>, etc.</p>
</li>
<li><p><strong>User-friendly web UI</strong> for viewing uptime history and logs.</p>
</li>
<li><p><strong>Multi-user support</strong> for team-based monitoring.</p>
</li>
<li><p><strong>Easy Docker deployment</strong> for quick setup.</p>
</li>
</ul>
<h3>Problem Statement</h3>
<p>Manual system monitoring is <strong>inefficient,</strong> if your system goes down, you need to be notified immediately.</p>
<h3>How This Automation Works:</h3>
<ul>
<li><p><strong>Uptime Kuma</strong> detects <strong>downtime</strong> and sends an <strong>alert email</strong> to <strong>Freshdesk</strong>.</p>
</li>
<li><p><strong>Freshdesk</strong> automatically <strong>creates a ticket</strong> based on the alert.</p>
</li>
<li><p>An <strong>automation rule</strong> in Freshdesk ensures that:</p>
</li>
</ul>
<ol>
<li><p>The <strong>ticket priority</strong> is set to <strong>Urgent</strong>.</p>
</li>
<li><p>The <strong>ticket type</strong> is classified as an <strong>Incident</strong>.</p>
</li>
<li><p>The ticket is <strong>assigned to a specific group</strong> for streamlined handling.</p>
</li>
<li><p>The ticket is <strong>automatically assigned to a designated agent</strong>.</p>
</li>
</ol>
<p>This setup <strong>removes the need for manual intervention</strong>, ensuring that critical system outages are promptly addressed by the right team. It also <strong>enhances incident response time</strong> by guaranteeing that no downtime goes unnoticed.</p>
<hr />
<h3>Step 1: Deploy Uptime Kuma with Docker Compose</h3>
<p>Create a directory for <strong>Uptime Kuma</strong> and a <code>docker-compose.yml</code> file:</p>
<pre><code class="language-bash">mkdir -p ~/uptime-kuma &amp;&amp; cd ~/uptime-kuma
</code></pre>
<pre><code class="language-bash">nano docker-compose.yml
</code></pre>
<p>Paste the following configuration in your <code>docker-compose.yml</code> file:</p>
<pre><code class="language-yaml">---
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1  # NB: ALWAYS use pinned versions
    container_name: uptime-kuma
    restart: always

    ports:
      - "3001:3001"

    volumes:
      - uptime-kuma-data:/app/data  # persistent storage

    environment:
      - TZ=Africa/Johannesburg  # set to your local timezone
      - UMASK=0022  # file permission control

    networks:
      - kuma_network

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001"]
      interval: 30s
      retries: 3
      start_period: 10s
      timeout: 5s

    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  uptime-kuma-data:

networks:
  kuma_network:
    driver: bridge
</code></pre>
<p>Save and exit (<code>CTRL+X</code>, <code>Y</code>, <code>Enter</code>), then start the container:</p>
<pre><code class="language-bash">docker compose up -d
</code></pre>
<p>Make sure uptime kuma is running:</p>
<pre><code class="language-bash">docker ps
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/68a5e6b9bf57f369891da8e0/62370a26-76ea-45a5-9b12-9b4c04a5a111.png" alt="" style="display:block;margin:0 auto" />

<p>Container status is healthy</p>
<p>Access <strong>Uptime Kuma</strong> at:<br /><code>http://&lt;your-host-ip&gt;:3001</code></p>
<p>Create your admin account:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772449990/9e01aba1-400c-49e9-a7b5-257164ba8a04.png" alt="" />

<hr />
<h3>Step 2: Adding a system to monitor</h3>
<ol>
<li><p>Click on the <strong>“Add New Monitor”</strong> button at the top-left.</p>
</li>
<li><p>In the <strong>“Add New Monitor”</strong> window, fill in the details:</p>
</li>
<li><p><strong>Monitor Type:</strong> Choose <code>HTTP(s)</code> for website monitoring.</p>
</li>
<li><p><strong>Name:</strong> Enter a descriptive name for your monitor (e.g., <code>Network IP Scanner</code>).</p>
</li>
<li><p><strong>URL:</strong> Enter your website URL (e.g., <code>[https://mylan.com]</code><a href="https://mysite.com%29.">.</a></p>
</li>
<li><p><strong>Method:</strong> Select <code>GET</code> (default).</p>
</li>
<li><p><strong>Heartbeat Interval:</strong> Set the time interval for Uptime Kuma to check your site (e.g., 30 seconds).</p>
</li>
<li><p><strong>Retries:</strong> Configure how many times Kuma should retry if the check fails.</p>
</li>
<li><p><strong>Notification Settings:</strong> Choose the method you want to use for your alerts, in this demo, I’m using Email(SMTP).</p>
</li>
<li><p><strong>Tags:</strong> (Optional) Add tags to organise your monitors.</p>
</li>
<li><p>Click <strong>“Save”</strong> to start monitoring.</p>
</li>
<li><p>The monitor should now appear in your <strong>Uptime Kuma dashboard</strong>.</p>
</li>
<li><p>If the site is online, it should show a green <strong>“UP”</strong> status(screenshot below):</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772451684/3a15effe-2af2-4d8f-a0bf-b6605b6d6fae.png" alt="" />

<hr />
<h3>Step 3: Configure Notifications (Email)</h3>
<ol>
<li><p>Go to <strong>Settings &gt; Notification</strong>.</p>
</li>
<li><p>Click <strong>Add New Notification</strong> and select a notification method (<strong>Email SMTP</strong>), this requires a valid user mailbox along with your SMTP provider settings.</p>
</li>
</ol>
<blockquote>
<p><strong>Note</strong>: I’ve added my Freshdesk email address in the <strong>To:</strong> field.</p>
</blockquote>
<p>3. Follow your email provider setup instructions and save the settings.</p>
<p>4. Link the notification to the service you want to monitor.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772453126/17341450-08c9-4f44-a936-c567663ca247.png" alt="" />

<h3>Verifying ticket creation after an outage(screenshot below)</h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772454699/d8e01af2-9d73-4c28-bd64-628a2c5a1a68.png" alt="" />

<p>As you can see, Uptime Kuma has successfully created a ticket. However, Freshdesk applies default settings, requiring manual updates that can be easily overlooked as more tickets come in. <strong>Let’s automate this to streamline the process</strong>.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772455966/25191b13-1210-4596-9278-4a7de6d34c56.png" alt="" />

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772457518/3d686f0d-f465-4482-a969-ff0d89aeb7c4.png" alt="" />

<p>Uptime Kuma ticket created in Freshdesk with default settings.</p>
<hr />
<h3>Step 4: Automatically Assign Tickets and Set Priority to Urgent</h3>
<p>To ensure downtime alerts are handled efficiently, we’ll create an <strong>automation rule</strong> in Freshdesk to automatically assign tickets and set the appropriate priority.</p>
<h4><mark class="bg-yellow-200 dark:bg-yellow-500/30">Steps to Create the Rule:</mark></h4>
<ol>
<li><p>In Freshdesk, navigate to <strong>Admin → Automations</strong>.</p>
</li>
<li><p>Click <strong>New Rule</strong> and give it a name, e.g., <strong>“Uptime Kuma Alerts”</strong>.</p>
</li>
<li><p>Apply the following <strong>conditions and actions</strong>:</p>
</li>
</ol>
<h4><mark class="bg-yellow-200 dark:bg-yellow-500/30">Rule Configuration:</mark></h4>
<p><strong>Event:</strong></p>
<ul>
<li>When a <strong>ticket is created.</strong></li>
</ul>
<p><strong>Conditions:</strong></p>
<p>If <strong>Requester Email</strong> is <code>*uptime_kuma@yourdomain.com*</code></p>
<ul>
<li>AND if <strong>Subject</strong> contains <code>[? Down]</code></li>
</ul>
<p><strong>Actions:</strong></p>
<ul>
<li><p>Set <strong>Type</strong> to <strong>Incident</strong></p>
</li>
<li><p>Set <strong>Priority</strong> to <strong>Urgent</strong></p>
</li>
<li><p>Assign to <strong>Group: Technical Support</strong></p>
</li>
<li><p>Assign to <strong>Agent: Luqmaan</strong></p>
</li>
</ul>
<blockquote>
<p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Note:</mark></strong> Before creating this rule, identify a <strong>consistent part of the subject line</strong> that never changes (e.g., <code>*[? Down]*</code>). First, test how Uptime Kuma formats its email subjects by allowing it to generate a ticket. Then, use that fixed pattern in your rule to ensure accuracy.</p>
</blockquote>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772459236/b2c181fb-2822-4022-afd1-d9da36e516cc.png" alt="" />

<p>After previewing, saving, and enabling the rule, your configuration should look like this:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772460448/1a95dc3d-f656-4389-b03b-3b23554dc23f.png" alt="" />

<blockquote>
<p>You can create multiple rules to customise ticket handling for different services, ensuring that only specific alerts have predefined priorities.</p>
</blockquote>
<hr />
<h3>Final Testing</h3>
<p>The automation is now in action. After testing, the rules worked exactly as expected where tickets were created with the correct priority, type, and assignments. Your system is now set up for seamless incident management.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772462090/9c740fd6-51c2-4545-8d60-ecdab0d926ec.png" alt="" />

<p>Our automation rules are now in action, ensuring immediate response when a critical system goes offline.</p>
<h3>Taking It a Step Further</h3>
<p>To fully automate the incident lifecycle, feel free to create another Freshdesk automation rule that <strong>automatically closes tickets</strong> when Uptime Kuma detects the service is back online. Since Uptime Kuma raises a separate alert when the system recovers, you can use a similar rule to match the subject line and set the ticket status to <strong>Closed</strong>.</p>
<h3>Conclusion</h3>
<p>Integrating Uptime Kuma with Freshdesk creates a basic incident workflow where downtime is detected, a ticket is generated, and ownership is assigned without manual handling.</p>
<p>This reduces the need for constant monitoring and keeps incident tracking consistent.</p>
<p>The same approach applies beyond this specific setup. The core value is in understanding how systems can communicate, trigger actions, and remove manual steps from operational workflows.</p>
]]></content:encoded></item><item><title><![CDATA[Adopting Proxmox VE as a Virtualisation Platform]]></title><description><![CDATA[VirtualBox served as my primary tool for desktop virtualisation for several years. It provided an accessible platform for testing operating systems, developing software locally, and exploring new envi]]></description><link>https://blog.tech-journey.co.za/why-proxmox-ve-became-my-new-home-after-virtualbox</link><guid isPermaLink="true">https://blog.tech-journey.co.za/why-proxmox-ve-became-my-new-home-after-virtualbox</guid><category><![CDATA[proxmox]]></category><category><![CDATA[self-hosted]]></category><category><![CDATA[Homelab]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Wed, 22 Jan 2025 20:08:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772379897/d8d8ba67-9213-4747-9c9d-802548a34c81.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>VirtualBox served as my primary tool for desktop virtualisation for several years. It provided an accessible platform for testing operating systems, developing software locally, and exploring new environments. It remains a functional, free solution for basic desktop requirements. However, as my infrastructure needs matured alongside my software development practices, the limitations of a desktop grade hypervisor became apparent. This necessitated a shift toward a more robust, enterprise level solution, resulting in my migration to Proxmox Virtual Environment (VE). This transition was driven by specific technical requirements regarding performance, automation, and scalability, marking a definitive move from a desktop utility to a bare metal hypervisor.</p>
<hr />
<h2><strong>The Architectural Differences Between Type 2 and Type 1 Hypervisors</strong></h2>
<p>VirtualBox operates as a Type 2 hypervisor, similar to VMware Workstation. It requires an underlying host operating system to function. This architecture inherently introduces a layer of software abstraction between the virtual machines and the physical hardware. Hardware resource allocation is brokered through the host operating system kernel. When running multiple intensive virtual machines concurrently, this brokering process creates noticeable performance bottlenecks. Specifically, I experienced significant latency with disk I/O operations and restricted network throughput, even when the underlying physical hardware had ample resources available.</p>
<p>Conversely, Proxmox VE operates as a Type 1 bare metal hypervisor based on Debian Linux. It installs directly onto the hardware, bypassing the need for a separate desktop host operating system. This architecture allows the hypervisor to allocate CPU cycles, memory, and storage directly to the virtual machines. The reduction in software overhead translates directly into measurable improvements in performance, lower system latency, and highly efficient resource utilisation. This bare metal approach is critical when simulating production environments accurately for application development and testing.</p>
<hr />
<h2><strong>Integrating Kernel based Virtual Machines and Linux Containers</strong></h2>
<p>A primary technical driver for adopting Proxmox VE was its native, dual support for both Kernel based Virtual Machines (KVM) and Linux Containers (LXC). While VirtualBox is restricted to full hardware virtualisation, Proxmox VE provides the flexibility to choose the most appropriate isolation method for a specific workload.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772376246/24e4ddad-dc51-44b1-bd54-d7550a795a36.png" alt="" />

<p>LXC containers offer a significantly lighter footprint compared to traditional virtual machines. Because containers share the host system kernel and <strong>isolate</strong> <strong>processes</strong> via <a href="https://blog.nginx.org/blog/what-are-namespaces-cgroups-how-do-they-work">cgroups</a> and <a href="https://blog.nginx.org/blog/what-are-namespaces-cgroups-how-do-they-work">namespaces</a>, they consume a fraction of the CPU and RAM. Boot times are nearly instantaneous. This architecture is optimal for deploying independent microservices, database instances, or continuous integration runners where the overhead of an entire guest operating system is redundant. Managing both full KVM instances for legacy applications and LXC containers for modern, lightweight services from a unified platform has drastically improved my deployment workflow and maximised hardware efficiency.</p>
<hr />
<h3><strong>Advanced Networking and Storage Capabilities</strong></h3>
<p>Beyond basic compute resource allocation, Proxmox VE excels in network and storage management. For application development, replicating complex network topologies is often necessary. Proxmox natively supports Linux bridges, Open vSwitch, and VLAN tagging. This allows for the logical segmentation of development, testing, and production simulation environments, mimicking real world network constraints and security policies.</p>
<p>Storage management is equally robust. While VirtualBox relies heavily on standard virtual disk images stored on a standard file system, Proxmox VE integrates deeply with advanced storage technologies. The native support for ZFS (Zettabyte File System) is particularly advantageous. ZFS provides software defined RAID, continuous integrity checking, and instantaneous snapshots. The ability to take immediate, block level snapshots of a database server before running a risky database migration script provides a highly resilient and forgiving development environment.</p>
<hr />
<h2><strong>Centralised Management and Automation via API</strong></h2>
<p>Managing a growing fleet of virtual machines via the VirtualBox GUI or basic CLI tools becomes inefficient at scale. Proxmox VE resolves this through a comprehensive web based management interface that handles clustering, storage, networking, and virtual machine state from a single control panel.</p>
<p>More importantly for a software development context, Proxmox VE is built around a fully functional REST API. Every action available in the web interface can be executed programmatically. This capability facilitates Infrastructure as Code (IaC) practices. By integrating tools like Terraform or Ansible, it becomes possible to define infrastructure requirements in code, version control those definitions, and provision identical development environments automatically. This level of automation is not feasible with standard desktop virtualisation tools and is a fundamental requirement for modern application development lifecycles.</p>
<p><strong>Enterprise Grade Features for Development Environments</strong></p>
<p>The platform also includes several enterprise grade features natively:</p>
<ul>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Live Migration</mark>:</strong> Virtual machines can be migrated between physical nodes in a cluster without interrupting service availability, which is useful for testing high availability application architectures.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">High Availability (HA)</mark>:</strong> Critical services can be configured to restart automatically on surviving nodes if a physical server fails.</p>
</li>
<li><p><strong><mark class="bg-yellow-200 dark:bg-yellow-500/30">Integrated Backup and Restore</mark>:</strong> Proxmox Backup Server integration or native VZDump capabilities allow for scheduled, deduplicated backups at the hypervisor level. This ensures rapid disaster recovery without relying on guest operating system agents.</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772377914/f1ec58c2-cc91-4dd3-87be-61d09cfe14b8.png" alt="" />

<hr />
<h2>The Community and Future-Proofing</h2>
<p>Finally, the strong and active community surrounding Proxmox VE was a significant draw. When encountering challenges or seeking best practices, the wealth of information available through forums, wikis, and documentation is immense. This vibrant ecosystem provides a sense of confidence that the platform will continue to evolve and be well-supported in the long term.</p>
<p>While VirtualBox remains an excellent tool for quick, single-user virtualisation on a desktop, for anyone looking to build a more robust, scalable, and efficient home lab or small-scale server environment, Proxmox VE is a clear winner. The transition was a learning curve, but the benefits in performance, flexibility, and management have been undeniable. My home lab has truly found its new, more powerful home and it’s very unlikely that I’ll be turning back.</p>
]]></content:encoded></item><item><title><![CDATA[Part 2: Organising Your Digital Workspace with Homepage]]></title><description><![CDATA[Bringing Order to Your Digital Workspace
Introduction
While keeping a physical workspace tidy is essential for focus and productivity, the same principles apply to our digital environments. Over time,]]></description><link>https://blog.tech-journey.co.za/part-2-organising-your-digital-workspace-with-homepage</link><guid isPermaLink="true">https://blog.tech-journey.co.za/part-2-organising-your-digital-workspace-with-homepage</guid><category><![CDATA[Homelab]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Docker compose]]></category><category><![CDATA[automation]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Mon, 06 Jan 2025 19:17:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772487205/214e8fad-e637-4d98-babd-d8f40abfd09d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Bringing Order to Your Digital Workspace</h2>
<h3>Introduction</h3>
<p>While keeping a physical workspace tidy is essential for focus and productivity, the same principles apply to our digital environments. Over time, I realised that having multiple browser tabs open, scattered bookmarks, and disorganised files slowed me down just as much as a cluttered desk. To streamline my workflow, I explored <a href="https://gethomepage.dev/"><strong>Homepage</strong></a>, a self-hosted dashboard that centralises access to all essential work resources.</p>
<h3>Why Use a Self-Hosted Dashboard?</h3>
<p>If you frequently juggle multiple tools, platforms, and websites, a dashboard can significantly improve efficiency. Instead of rummaging through bookmarks or typing URLs manually, everything is accessible in one place. Here’s how <strong>Homepage</strong> helped me bring order to my digital workspace:</p>
<h3>“What’s this ‘Homepage’ you speak of?”</h3>
<ul>
<li>It’s a lightweight, highly customisable dashboard that acts as a central hub for quick access to applications, services, and web links.</li>
</ul>
<p>Let’s dive into it*..*</p>
<hr />
<h3>1. Deploying with Docker</h3>
<p>Since I run most of my self-hosted services in Docker, setting up Homepage is pretty straightforward.</p>
<p>Here’s how you can deploy it:</p>
<pre><code class="language-bash"># create a directory called homepage and cd into it. 
mkdir -p ~/dashboard/homepage &amp;&amp; cd ~/dashboard/homepage
</code></pre>
<pre><code class="language-bash">#create a docker-compose.yml file 
nano docker-compose.yml
</code></pre>
<p>Paste the following configuration(be sure to read the comments):</p>
<pre><code class="language-yaml">---
services:
  homepage:
    image: ghcr.io/gethomepage/homepage:v0.9.2
    container_name: homepage
    ports:
      - 3000:3000
    env_file: .env # Make sure to create this .env file in your current working directory (use touch .env)
    volumes:
      - ./config:/app/config # Homepage will create this for you.
      - /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations, see alternative methods
    environment:
      PUID: $PUID # Set this to your user ID (you can find it by running `id -u`)
      PGID: $PGID # Set this to your group ID (you can find it by running `id -g`)
      TZ: Africa/Johannesburg # set to your own timezone
    restart: unless-stopped
</code></pre>
<hr />
<h3>2. Accessing the default dashboard</h3>
<p>Once deployed, you can access the dashboard via <code>[http://&lt;your-server-ip&gt;:3000]</code></p>
<p>Homepage has a user-friendly YAML-based configuration file, making it easy to customise layouts, themes, categories and all sorts*.*</p>
<blockquote>
<p>Note: The icons we will be using are sourced from <a href="https://github.com/homarr-labs/dashboard-icons/blob/main/ICONS.md">Homarr Labs Dashboard Icons</a>, where you can explore a wide variety of icons to further personalise your dashboard.</p>
</blockquote>
<p>Your default dashboard should look like this:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772479181/6e1befbc-7bbc-4cba-ad16-163807cdc9d7.png" alt="" />

<hr />
<h3>3. Updating the <code>services.yaml</code> File</h3>
<p>Now let’s customise the dashboard with your own services.</p>
<h4>Step 1: Locate the <code>services.yaml</code> file in the config directory:</h4>
<pre><code class="language-bash">cd ~/dashboard/homepage/config &amp;&amp; nano services.yaml
</code></pre>
<h4>Step 2: Add Your Services</h4>
<p>Now, you can copy and paste the following configuration into the <code>services.yaml</code> file. The example config below will organise your dashboard with categories for project management, automation, cloud services, and networking but feel free to customise it according to your needs:</p>
<pre><code class="language-yaml">services:
  - Project Management:
      - Asana:
          icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/asana.png
          href: http://app.asana.com
          description: Task &amp; project management
      - Jira:
          icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jira.png
          href: http://your-jira-link-here
          description: Agile project tracking
      - Notion:
          icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/notion.png
          href: http://your-notion-link-here
          description: Notes, docs, and collaboration

  - Automation &amp; Workflows:
      - n8n:
          icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/n8n.png
          href: http://your-n8n-link-here
          description: Workflow automation

  - Cloud Services:
      - Azure:
          icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/azure.png
          href: http://portal.azure.com
          description: Microsoft cloud platform
      - Microsoft 365:
          icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/microsoft-365.png
          href: http://your-microsoft-365-link-here
          description: Cloud productivity suite
      - SharePoint:
          icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/microsoft-sharepoint.png
          href: http://your-sharepoint-link-here
          description: Document management &amp; collaboration

  - Networking &amp; Proxy:
      - Nginx Proxy Manager:
          icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nginx-proxy-manager.png
          href: http://your-nginx-proxy-manager-link-here
          description: Reverse proxy &amp; SSL management
</code></pre>
<h4>Step 3: Save and Close the File</h4>
<p>Save and close the file (in nano: press <code>Ctrl + O</code>, <code>Enter</code> to save, then <code>Ctrl + X</code> to exit).</p>
<h4>Step 4: Reload the page or restart the container:</h4>
<pre><code class="language-bash"># make sure you're in the homepage directory
docker compose restart
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772480729/865c9845-dc82-44b3-b394-3c185a39ede5.png" alt="" />

<p>Tools and services all neatly organised into categories.</p>
<hr />
<h3>4. Adding a Background Image</h3>
<p>Homepage allows you to easily add a custom background image via the <code>settings.yaml</code> file. This is a great way to personalise your dashboard further. Follow these steps to add your background image:</p>
<h4>Step 1: Locate the <code>settings.yaml</code> file:</h4>
<pre><code class="language-bash">cd ~/dashboard/homepage/config &amp;&amp; nano settings.yaml
</code></pre>
<h4>Step 2: Add the Background Image Setting:</h4>
<pre><code class="language-yaml">background:
  image: https://images.unsplash.com/photo-1502790671504-542ad42d5189?auto=format&amp;fit=crop&amp;w=2560&amp;q=80
</code></pre>
<p>You can also adjust filters (blur, brightness, saturation) to achieve the look you want. These settings are based on Tailwind CSS classes, so you can use the values described in the <a href="https://tailwindcss.com/docs/filter">Tailwind CSS documentation</a>.</p>
<blockquote>
<p>P.S I’m no expert in this regard, just following the documentation <em>🙂</em></p>
</blockquote>
<p>For more detailed information on background image settings, including filter options, check out the <a href="https://gethomepage.dev/configs/settings/#background-image">Homepage docs</a> and <a href="https://unsplash.com/">Unsplash</a> (make sure to <strong>copy the image URL</strong> when referencing it in your settings.yaml file):</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772482421/fb7036a6-5e34-4992-a29e-451597f9a392.png" alt="" />

<h4>Step 3: Save and Close the File</h4>
<h4>Step 4: Restart Homepage:</h4>
<pre><code class="language-bash">docker compose restart
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772485021/8b8c6d09-2772-46ed-931f-b7519a1d6216.png" alt="" />

<hr />
<h3>Conclusion</h3>
<p>Just as a tidy desk improves focus, an organised digital environment boosts productivity.</p>
<p>Don’t shy away from exploring more customisations, integrations, and features to truly tailor your dashboard to fit your needs. The possibilities are endless, and with Homepage, you have the control to make your workspace as productive and personalised as you want it to be.</p>
]]></content:encoded></item><item><title><![CDATA[Runtipi: Self-Hosting Made Incredibly Simple]]></title><description><![CDATA[Why Self-Hosting Shouldn’t Be a Headache
Have you ever considered self-hosting your own apps but got overwhelmed by endless Docker commands, YAML files, and troubleshooting? You’re not alone. While th]]></description><link>https://blog.tech-journey.co.za/runtipi-self-hosting-made-incredibly-simple</link><guid isPermaLink="true">https://blog.tech-journey.co.za/runtipi-self-hosting-made-incredibly-simple</guid><category><![CDATA[Homelab]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Docker compose]]></category><category><![CDATA[self-hosted]]></category><dc:creator><![CDATA[Luqmaan Marthinus]]></dc:creator><pubDate>Sun, 05 Jan 2025 21:18:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772405176/0f10dc2b-cc3b-4c0c-a6d4-5511e30c1fad.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Why Self-Hosting Shouldn’t Be a Headache</h3>
<p>Have you ever considered self-hosting your own apps but got overwhelmed by endless Docker commands, YAML files, and troubleshooting? You’re not alone. While the reward of getting everything to work is great, the journey can be frustrating. I know the pain of trying to set up a simple service, only to hit roadblocks that eat up hours of time.</p>
<p>That’s where Runtipi comes in. It eliminates the hassle and lets you deploy self-hosted apps with ease, even if you’ve never touched Docker before(you’re missing out though 😄).</p>
<h3>What is Runtipi, and Why Should You Care?</h3>
<p>Runtipi simplifies self-hosting by handling container management, reverse proxy setup, SSL certificates, and app installations , all from an easy-to-use web dashboard. No need to get lost in config files or manually install dependencies. Just choose an app, hit install, and you’re good to go.</p>
<h3>Who’s Runtipi For? (Spoiler: Almost Everyone 🙂)</h3>
<h3>1. Newbies Who Want a Frictionless Start</h3>
<p>Want to self-host without feeling like you need a degree in DevOps? Runtipi has your back.</p>
<h3>2. Techies Who Don’t Want to Waste Time</h3>
<p>You know your way around a terminal, but you don’t always have time to manually configure <code>docker-compose.yml</code> files for every app.</p>
<h3>3. Home Lab Enthusiasts Who Value Simplicity</h3>
<p>Tinkering is fun, but spending hours debugging container issues is the worst. Runtipi keeps things simple so you can spend more time <strong>USING</strong> your apps.</p>
<h3>Getting started:</h3>
<h3>Step 1: Installing Runtipi</h3>
<p>Run this single command on your Linux server:</p>
<pre><code class="language-bash">curl -L https://setup.runtipi.io | bash
</code></pre>
<h3>Step 2: Open the Web Dashboard</h3>
<p>Once installed, navigate to <a href="http://your-server-ip">http://your-server-ip</a> in your browser to access the Runtipi web dashboard (it uses the default HTTP protocol, so there’s no need to specify a port).</p>
<h3>Step 3: Demo</h3>
<blockquote>
<p>For a quick overview, check out the demo <a href="https://drive.google.com/file/d/1bv-KBvgVZq4Yty9hkOktcwBui1MgLjT4/view?usp=drive_link"><strong>here</strong></a>. Note that during this demo, I wasn’t prompted to set up an account, as I had already tested the setup prior to posting this blog. If you’re setting it up fresh, you might be asked to create an account, which is part of the usual process.</p>
</blockquote>
<h3>Step 3: Deploy an App</h3>
<p>Choose any app from the list and install it with just one click. It’s that easy! Of course, you can always add extra layers of security if you want.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755772403232/c983eeac-32ae-4a0a-911f-bb4b634b5cd1.png" alt="" />

<p>Example of how easy it is to install an app.</p>
<h3>Runtipi Makes Self-Hosting Easier, But Challenges are Worth Embracing</h3>
<p>While Runtipi simplifies the process of deploying self-hosted applications with its one-click install feature, it’s important to remember that there’s value in tackling challenges. <strong>Efficient workflows</strong> are essential, but <strong>embracing complex problems and finding innovative solutions also leads to growth</strong>. This setup makes it easier for everyone, from newcomers to seasoned pros, but don’t shy away from opportunities to expand your skills by tackling more intricate tasks.</p>
<h3>Ready to Give It a Shot?</h3>
<p>Head over to <a href="https://runtipi.io/">Runtipi’s official website</a> and see for yourself. Self-hosting doesn’t have to be complicated! 🙂🕺💃</p>
]]></content:encoded></item></channel></rss>