MoreRSS

site iconThe Practical DeveloperModify

A constructive and inclusive social network for software developers.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of The Practical Developer

Debug a Donation Form

2026-02-05 17:40:15

Continuing with the accessibility theme, I tackled a lab from freeCodeCamp where I debugged a Donation Form. Like all labs in the curriculum, this one is guided by user stories - five in total this time - which you follow to hopefully meet all the checks and complete the exercise.

Below is the initial code provided at the start of the lab, complete with all of the accessibility issues. Can you spot any of them?

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Donation Form</title>
</head>
<body>
  <h1>Donation Form</h1>
  <form>

    Full Name:
    <input type="text" name="name"></input>

    Email Address:
    <input type="text" name="email">

    Donation Amount ($):
    <input type="number" name="amount"></input>

    <input type="checkbox" name="newsletter"></input>
    Subscribe

    <input type="submit" value="Send"></input>
  </form>
</body>
</html>

The user stories themselves provide plenty of hints about the errors - for example, reminding you that input elements are void elements and therefore shouldn't have closing tags. That's when I noticed that several input elements were missing label elements where appropriate, as confirmed by the second user story.

Moving on to the third user story, I noticed the email input type was incorrect - if you hadn't spotted that already. Once that was fixed, the next task was ensuring each label element was correctly linked to its corresponding input element, as outlined in the fourth user story.

The final user story asked you to add the required attribute where appropriate, and once that was done, all the checks passed. Below is the completed lab after making all the changes:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Donation Form</title>
</head>
<body>
  <h1>Donation Form</h1>
  <form>
    <label for="full-name">
      Full Name:
      <input type="text" id="full-name" name="name" required>
    </label>

    <label for="email-address">
      Email Address:
      <input type="email" id="email-address" name="email" required>
    </label>

    <label for="donation-amount">
      Donation Amount ($):
      <input type="number" id="donation-amount" name="amount" required>
    </label>

    <label for="subscribe">
      Subscribe
      <input type="checkbox" id="subscribe" name="newsletter">
    </label>

    <input type="submit" value="Submit">
  </form>
</body>
</html>

Next up in the freeCodeCamp Responsive Web Design certification are a series of theory lessons on ARIA, followed by a workshop where you'll build an Accessible Audio Controller. I'll dive into those in the next update!

Written by a Human logo

AWS European Sovereign Cloud: Beyond data sovereignty

2026-02-05 17:35:04

When I wrote about AWS' Digital Sovereignty Pledge earlier, I approached it primarily from a data perspective. I focused on where your data lives, who can access it and how you can control it. That made sense to me at the time. Data sovereignty felt like the whole story.

Recent global developments have taught me otherwise. The world has become a more unpredictable place. We've seen how quickly geopolitical tensions can escalate and how supply chains can become leverage points. I've come to understand that sovereignty isn't just about data. It has supply chain, legal and operational dimensions that are equally important.

This realisation is what makes AWS European Sovereign Cloud (ESC) so relevant today. It addresses all these angles in ways I hadn't fully appreciated before.

Historical perspective: not a reaction, but a roadmap

Before diving into what ESC offers, it's worth understanding that this isn't a knee-jerk response to recent political changes. AWS began developing ESC well before the current US administration took office. This matters because it shows deliberate, long-term planning rather than reactive scrambling.

AWS has built sovereign cloud offerings before. They created GovCloud for US federal agencies that need to meet strict compliance requirements. They established a separate China region, completely decoupled from the rest of AWS infrastructure. These weren't experiments. They were proof points that AWS could deliver full cloud capabilities within specific sovereignty boundaries.

ESC follows this same pattern, but it's designed for European organisations with European requirements. The planning started years ago. The recent geopolitical shifts have simply made the need more urgent and the value more obvious.

The supply chain challenge: facing reality

Let's be honest about something uncomfortable. All server hardware has components produced in China. The most advanced chips come from the US. There's no escaping this reality. You can't build a modern data centre without touching these supply chains.

So when AWS talks about sovereignty, they're not pretending they've solved the unsolvable. They're being pragmatic about what's actually achievable.

Here's what they've done instead. AWS has made all their hardware designs and software code for ESC available. This is the 'red button' scenario. In the extremely unlikely event that access to AWS infrastructure or supply chains is cut off, European operators would have everything they need to continue running the service.

Is this perfect? No. But it's honest. It acknowledges the constraints whilst providing the strongest possible mitigation. That matters more than impossible promises.

The legal dimension: where jurisdiction actually means something

This is where ESC gets interesting from a governance perspective. The entire ESC operation is captured within a German legal entity. Not a subsidiary that ultimately answers to Seattle. A German entity operating under German law.

Yes, the US can invoke the CLOUD Act. That's a fact. But here's the crucial difference: they would need to go through German courts to enforce it. They would need German judges to grant those requests. And to date, US authorities have never successfully compelled data access this way, even through US courts.

This isn't theoretical protection. It's a genuine legal barrier. European data protection authorities understand this. It's why they can be more comfortable with ESC than with standard AWS regions.

The legal structure creates real friction for any attempt at extraterritorial data access. That friction is the point.

The operational reality: European staff only

The operational sovereignty piece is refreshingly straightforward. ESC is staffed entirely by European citizens. No outsourcing to India for cost savings. No escalations to US headquarters for certain types of issues. Everything is handled within Europe by Europeans.

This might sound simple, but the implications are significant. It means conversations about your infrastructure, your data and your compliance needs happen with people who understand European regulatory frameworks firsthand. They're not reading from a script developed elsewhere.

It also means that all support and maintenance work to keep the cloud available stays within European jurisdiction. There's no scenario where someone in Seattle needs to be involved in maintaining the infrastructure that underpins your workloads.

What stays the same: the good bits

Here's what ESC doesn't change: the quality of the infrastructure, the breadth of services and the pace of innovation.

You still get the same AWS services you'd get in Frankfurt or Ireland. The same security capabilities I discussed in my earlier blog post still apply. You can still encrypt everything everywhere. You still have control over where your data lives and who can access it.

ESC isn't a stripped-down version of AWS. It's AWS with an additional layer of sovereignty protection. The cloud is just as resilient. The services are just as innovative. The performance is just as strong.

What you're adding is supply chain transparency, legal independence and operational control. You're not trading away capability to get it.

Why this matters now

The world has become more volatile in recent years. That's not political commentary. It's just observation. We've seen how quickly stable relationships can become contentious. We've watched supply chains that seemed unshakeable prove fragile.

European organisations need to plan for a world where digital infrastructure might become caught up in geopolitical disputes. ESC provides a credible answer to that risk without requiring you to abandon the cloud or compromise on capability.

It's not paranoia to consider these scenarios anymore. It's prudence.

Getting started

If sovereignty beyond just data protection matters to your organisation, ESC deserves serious consideration. The supply chain transparency, legal structure and operational independence it provides are genuine differentiators.

The time to think about these questions is before you need the answers. If you want to explore how ESC could work for your specific requirements, I'm happy to discuss it.

The conversation isn't about whether sovereignty matters. Recent years have settled that question. The conversation is about what sovereignty actually means in practice and how you achieve it without sacrificing innovation.

ESC is AWS' answer to that question for European organisations. It's not perfect because nothing is. But it's thoughtful, comprehensive and genuine. And right now, that's what matters.

When NOT to Use `@EnvironmentObject` in SwiftUI

2026-02-05 17:33:59

If you've been working with SwiftUI for any length of time, you've probably reached for @EnvironmentObject at some point. It's convenient, it's clean, and it makes passing data down a deep view hierarchy feel almost effortless. But like most powerful tools, it comes with trade-offs that aren't always obvious until you've shipped a bug to production.

This article isn't about bashing @EnvironmentObject. It's about understanding it well enough to know when it's the wrong choice.

What is @EnvironmentObject, really?

Before we talk about when not to use something, we need to be clear about what it actually does.

@EnvironmentObject is a property wrapper that lets a view access a shared object that's been injected into the SwiftUI environment. The object must conform to ObservableObject, and when any of its @Published properties change, all views observing that object will re-render.

Here's a minimal example:

class UserSettings: ObservableObject {
    @Published var username: String = "Guest"
}

struct ParentView: View {
    @StateObject private var settings = UserSettings()

    var body: some View {
        ChildView()
            .environmentObject(settings)
    }
}

struct ChildView: View {
    var body: some View {
        GrandchildView()
    }
}

struct GrandchildView: View {
    @EnvironmentObject var settings: UserSettings

    var body: some View {
        Text("Hello, \(settings.username)")
    }
}

Notice that ChildView doesn't know anything about UserSettings. The object skips right over it and lands in GrandchildView. That's the appeal: you don't have to thread dependencies through every intermediate view.

How does it work under the hood?

When you call .environmentObject(settings), SwiftUI stores a reference to that object in the view's environment dictionary, keyed by the object's type. When a child view declares @EnvironmentObject var settings: UserSettings, SwiftUI looks up UserSettings.self in the environment and hands back the reference.

If it can't find a matching object, your app crashes. No compile-time warning. No graceful fallback. Just a runtime crash with a message like:

Fatal error: No ObservableObject of type UserSettings found.

This behavior is intentional. SwiftUI treats a missing environment object as a programmer error, not a recoverable condition.

When NOT to use @EnvironmentObject

Now that we're on the same page about what @EnvironmentObject does, let's get into the situations where you should think twice before using it.

1. When the dependency is only needed by one or two views

If your shared object is only consumed by a single view, or by a parent and its immediate child, @EnvironmentObject adds indirection without providing benefit.

Consider this case:

struct ProfileView: View {
    @EnvironmentObject var userProfile: UserProfile

    var body: some View {
        VStack {
            Text(userProfile.name)
            Text(userProfile.email)
        }
    }
}

If ProfileView is the only consumer of UserProfile, you've introduced implicit coupling. Someone reading the code has to trace back through the view hierarchy to find where the object was injected. Compare that to:

struct ProfileView: View {
    let userProfile: UserProfile

    var body: some View {
        VStack {
            Text(userProfile.name)
            Text(userProfile.email)
        }
    }
}

Now the dependency is explicit. You can see what ProfileView needs just by looking at its declaration. This matters when your codebase grows and you're trying to understand what a view depends on without running the app.

Use explicit parameters when the object doesn't need to travel through multiple layers of the view hierarchy.

2. When working with reusable components

This one bites people regularly. You build a reusable view component, say a custom button or a card layout, and you wire it up with @EnvironmentObject because it needs some shared state.

struct FavoriteButton: View {
    @EnvironmentObject var favorites: FavoritesManager
    let itemID: String

    var body: some View {
        Button(action: { favorites.toggle(itemID) }) {
            Image(systemName: favorites.contains(itemID) ? "heart.fill" : "heart")
        }
    }
}

This works fine in your app. Then you try to use FavoriteButton in a SwiftUI preview:

struct FavoriteButton_Previews: PreviewProvider {
    static var previews: some View {
        FavoriteButton(itemID: "123")
        // Crash: No ObservableObject of type FavoritesManager found.
    }
}

You fix it by adding .environmentObject(FavoritesManager()). But now imagine you want to share this component with another project, or publish it as a package. Every consumer has to know about FavoritesManager and provide it, even if their favoriting logic is completely different.

Reusable components should take explicit dependencies through their initializer or use closures for actions. This keeps them portable and testable without requiring specific environment objects.

struct FavoriteButton: View {
    let isFavorited: Bool
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Image(systemName: isFavorited ? "heart.fill" : "heart")
        }
    }
}

Now the component is dumb about where its data comes from. The parent can wire it up however it wants.

3. When you need to test views in isolation

Unit testing SwiftUI views is already tricky. Adding @EnvironmentObject makes it trickier because you have to set up the environment for every test.

func testProfileDisplaysCorrectName() {
    let mockProfile = UserProfile(name: "Test User", email: "[email protected]")
    let view = ProfileView()
        .environmentObject(mockProfile)
    // Now you can test...
}

This isn't terrible for one test, but it compounds. If you have ten views that all use the same environment object, every test file needs to know how to construct and inject that object. If the environment object has its own dependencies, you're now setting up a whole object graph for what should be a simple view test.

Views with explicit dependencies are easier to instantiate in tests:

func testProfileDisplaysCorrectName() {
    let mockProfile = UserProfile(name: "Test User", email: "[email protected]")
    let view = ProfileView(userProfile: mockProfile)
    // Simpler.
}

4. When the object needs to exist conditionally

@EnvironmentObject assumes the object always exists. There's no optional variant built into the property wrapper. If you have a scenario where the object might not exist, for example user authentication state where the user object only exists after login, @EnvironmentObject becomes awkward.

Some developers work around this by creating a wrapper object that's always present but contains an optional:

class AuthState: ObservableObject {
    @Published var currentUser: User?
}

This works, but now every view that accesses AuthState has to handle the optional. You're essentially using @EnvironmentObject to pass around an optional, which feels like a mismatch between the tool and the problem.

If the dependency is conditional, consider passing it explicitly as an optional parameter, or restructuring your view hierarchy so that certain views only appear when the dependency exists.

5. When you're passing data into a NavigationLink destination

This is a subtle one. When you use NavigationLink, the destination view is created at the time you declare the NavigationLink, not when the user taps it. That means if your destination relies on @EnvironmentObject, the environment object must be available when the parent view's body is evaluated.

struct ItemListView: View {
    @EnvironmentObject var store: ItemStore

    var body: some View {
        NavigationStack {
            List(store.items) { item in
                NavigationLink(destination: ItemDetailView(item: item)) {
                    Text(item.name)
                }
            }
        }
    }
}

struct ItemDetailView: View {
    let item: Item
    @EnvironmentObject var store: ItemStore

    var body: some View {
        // Uses both item and store
    }
}

This usually works because the environment propagates down through NavigationStack. But if you're doing something unusual with your navigation structure, or if you're using NavigationLink outside of a NavigationStack, you can hit runtime crashes that are hard to diagnose.

More importantly, this creates tight coupling between your navigation structure and your dependency injection strategy. If you later decide to present ItemDetailView in a different context, say a sheet or a different navigation stack, you have to remember to inject the environment object there too.

6. When you need fine-grained observation

Here's a performance concern. When any @Published property on your environment object changes, every view observing that object gets marked for re-render. SwiftUI is smart about skipping unnecessary work, but it still has to evaluate each view's body to determine if the output changed.

class AppState: ObservableObject {
    @Published var username: String = ""
    @Published var notificationCount: Int = 0
    @Published var theme: Theme = .light
    @Published var recentSearches: [String] = []
    // ... and so on
}

If you have a large environment object with many published properties, and views throughout your app observe it, you can end up with more view re-evaluation than necessary. A change to notificationCount triggers body evaluation in a view that only cares about username.

For large apps, consider splitting your state into multiple focused objects, or using the newer @Observable macro (iOS 17+) which provides more granular observation at the property level.

7. When SwiftUI previews become painful

I mentioned previews earlier, but it's worth emphasizing. @EnvironmentObject requires you to set up the environment for every preview. If your environment object has dependencies, those need to be set up too. If your app has multiple environment objects, every preview needs all of them.

struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        SomeView()
            .environmentObject(UserSettings())
            .environmentObject(NetworkMonitor())
            .environmentObject(ThemeManager())
            .environmentObject(FeatureFlags())
    }
}

This gets old fast. Some teams end up creating preview helper functions or wrappers to avoid repeating this setup. That works, but it's ceremony that wouldn't exist if the views had explicit dependencies.

What to use instead

Depending on your situation, consider these alternatives:

Explicit initializer parameters are the simplest option. The dependency is visible, the view is easy to test, and there's no risk of runtime crashes from missing objects.

Closures for actions work well when a view needs to trigger something but doesn't need to know about the object that handles it. Pass a closure instead of the whole manager.

@StateObject at the point of use is appropriate when a view owns its state and doesn't need to share it with siblings.

@observable (iOS 17+) provides more efficient observation with less boilerplate than ObservableObject. Worth considering if you can target iOS 17 or later.

Custom EnvironmentKey with default values lets you create environment values that have sensible defaults, avoiding crashes when the value isn't explicitly provided.

Closing thoughts

@EnvironmentObject is a legitimate tool for passing shared state through a deep view hierarchy without prop drilling. It works well for truly app-wide concerns like authentication state, user preferences, or theme settings, things that many views need and that don't change frequently.

If you found this useful, I write about Swift, SwiftUI, and iOS development fairly regularly. Feel free to follow along.

AWS Zero to Hero: Learn AWS with Practical, Real-World Examples

2026-02-05 17:30:19

AWS Zero to Hero: Learn AWS with Practical, Real-World Examples

Learning

AWS can feel overwhelming — dozens of services, complex networking, security concepts, and production best practices that are rarely explained together.

To solve this, I created an AWS Zero to Hero playlist focused on practical, real-world AWS usage, not just theory.

📺 AWS Zero to Hero – Full Playlist:

👉 https://www.youtube.com/playlist?list=PLg_uqzorGE2VOQHks3D7fj1IcPFVvULq-

Why This Playlist Exists

Most AWS tutorials:

  • Explain services in isolation
  • Skip real-world misconfigurations
  • Don’t show how things break in production

This playlist is designed to help learners understand AWS the way it’s actually used in real systems — with security, networking, automation, and deployment in mind.

What You’ll Learn in This Series

The playlist covers AWS concepts step by step, including:

  • AWS core services (EC2, VPC, IAM, S3)
  • Real-world networking concepts
  • Security best practices and common misconfigurations
  • CI/CD and deployment workflows
  • Cloud architecture decisions used in production

Each video builds on the previous one, making it suitable for beginners as well as intermediate learners.

Learning Approach

This series focuses on:

  • Hands-on demonstrations
  • Real AWS console walkthroughs
  • Practical explanations instead of memorization
  • Common mistakes engineers make in real environments

The goal is not just to pass certifications, but to understand AWS deeply.

Who This Playlist Is For

  • Beginners starting with AWS
  • DevOps and Cloud engineers
  • Backend developers working with AWS
  • Students preparing for cloud roles
  • Anyone who prefers learning by doing

Recommended Way to Use This Playlist

  • Follow the videos in order
  • Pause and try things yourself in AWS
  • Revisit videos as you build your own projects
  • Use this as a long-term reference, not a one-time watch

Final Thoughts

AWS becomes much easier when you understand why services exist and how they work together.

If you’re looking for a clear, practical path from beginner to confident AWS user, this playlist should help.

If you find it useful:

  • Share feedback
  • Suggest topics you’d like covered
  • Save the playlist for future reference

Happy learning 🚀

Control/Lopping Statement : For loop and While loop

2026-02-05 17:29:16

What is for and while loop?

  • loops are used to repeat a block of code. For example, if we want to show a message 100 times,then rather than typing the same code 100 times, we can use a loop.
  • loop is used to run a specific code until a certain condition is met.

while loop:

  • A while loop evaluates the expression inside the parenthesis ().
  • If the expression evaluates to true, the code inside the while loop is executed.
  • The expression is evaluated again.
  • This process continues until the expression is false.
  • When the expression evaluates to false, the loop stops.

Example :
class LearWhileLoop{
public static void main(String[] args) {

int i = 1;
while(i <= 5) {
  System.out.println(i);
  i++;
}

}
}

output :
1
2
3
4
5

for loop:

  • The initialexpression initializes and/or declares variables and executes only once.
  • The condition is evaluated. If the condition is true, the body of the for loop is executed.
  • The updateexpression updates the value of initialexpression.
  • The condition is evaluated again. The process continues until the condition is false.

Example :

class LearForLoop{
public static void main(String[] args) {

int n = 5; 
for (int i = 1; i <= 5; ++i) {
  System.out.println(i);
}

}
}

Output:
1
2
3
4
5

  • We use for loop when we know how many times the loop should run.
  • We use while when the loop depends on a condition, not a fixed number.

Example :
public static void main(String args[]) {
{

    Scanner sc = new Scanner(System.in);
    boolean bl = true;
    while(bl)
    {
        System.out.print("Enter a character in small alphabet: ");
        char ch = sc.next().charAt(0);
        if(ch >='a' && ch <= 'z') {
            System.out.println("Good! you have entered a vowel");
            bl = false;
        }
        else
            System.out.println("Entered Character is not vowel");
    }

}

But we can also do this in for loop

public static void main(String args[])
    {

        Scanner sc = new Scanner(System.in);
        boolean bl = true;
        for (; bl; ) {
            System.out.print("Enter a character in small alphabet: ");
            char ch = sc.next().charAt(0);
            if (ch >= 'a' && ch <= 'z') {
                System.out.println("Good! you have entered a vowel");
                bl = false;
            } else
                System.out.println("Entered Character is not vowel");
        }

    }

Build a Real-World CI/CD Pipeline for Node.js using Docker, GitHub Actions, and AWS EC2

2026-02-05 17:25:35

Build a Real-World CI/CD Pipeline for Node.js using Docker, GitHub Actions, and AWS EC2

Most CI/CD tutorials explain tools in isolation.

In real projects, everything has to work together — application code, containerization, CI/CD automation, and cloud infrastructure.

In this post, I’m sharing a production-style CI/CD pipeline that deploys a Node.js application using Docker, GitHub Actions, and AWS EC2, end to end.

📺 Full video walkthrough (recommended):

👉 https://youtu.be/WwxSNIrW8bk

Why I Built This

When learning CI/CD, I noticed a common gap:

  • One tutorial explains Docker
  • Another explains GitHub Actions
  • Another explains EC2

But very few show how these pieces connect in a real deployment flow.

This project focuses on:

  • Practical setup
  • Real deployment sequence
  • Automation that mirrors real DevOps teams

What This Project Demonstrates

  • A simple Node.js application
  • Dockerizing the application
  • CI/CD pipeline using GitHub Actions
  • Automated deployment to an EC2 instance
  • A repeatable and maintainable workflow

This is not theory — it’s a working pipeline.

Architecture Overview


text
Developer pushes code
        ↓
GitHub repository
        ↓
GitHub Actions pipeline
  - Build Docker image
  - Run CI steps
  - Deploy to EC2
        ↓
Docker container running on EC2
        ↓
Application available to users