I Decoded a Project Challenge Just for Fun—and Didn’t Even Apply to the Job

by kleamerkuri

There’s something thrilling about tackling a challenge purely for the sake of learning.

No expectations, no deadlines—just a chance to flex those problem-solving muscles and dive into something new.

That’s exactly what I did when I came across a coding project designed for job applicants. Though I didn’t apply for the job (for one reason or another), I found the project too tempting to pass up.

Think of the challenge as a software engineering “capture the flag” training game, where you hunt for clues, decode hidden meanings, and put your logic and coding skills to the test.

I was always a fan of hidden object games growing up so this was right up my alley.

Most other projects I’ve had to complete for job interviews involved building either a small app or page based on user requirements—this was a rather novel approach.

As I worked to complete each step, I got excited to share my approach to getting a solution because I love hearing of other people’s steps as they navigate through a process to solve a problem. And I figure everyone else is the same 😉

In case you also share my nerdy interests or just need some guidance as you tackle a similar challenge, here’s how the journey unfolded.

Note ‼️
For obvious reasons (i.e. not to outright mess the job posting’s challenge), I will not share links, the word, or any other specific information. The focus of this post is to walk through the process and troubleshooting steps and revisit familiar concepts like building React components.

The Beginning: Decoding a Mysterious URL

The first step was to decode the project’s instructions URL, which was Base64 encrypted.

Base64 is an encoding scheme that converts binary data into text (and, yes, I Googled that; I don’t go around carrying a mental library of definitions so feel no shame looking up how to explain things you’ve encountered before). If you’ve ever worked with APIs or handled authentication tokens, you’ve probably bumped into it.

I entered the string into a quick decoder script and out came the link to the project requirements. It was a straightforward start, but it set the tone that this wasn’t going to be a typical “build a CRUD app” exercise. This was going to make me think 🧠

Step Two: Understanding the Requirements

The decoded URL led to a page with detailed instructions for the project. It was laid out like a riddle—there was a link to another page where hidden DOM nodes contained the pieces of the target URL.

My job was to identify those nodes, extract the pieces, and reconstruct the hidden URL.

Sounds easy, right? Well, not exactly.

The instructions hinted at patterns and logic governing which DOM nodes contained valid URL fragments.

I love a good puzzle, but this one came with its own set of traps. At first glance, I thought I had everything under control. Turns out, I didn’t 😒

The Plot Twist: Misunderstanding the Directions

My initial approach was… let’s call it “overconfident.” I quickly identified what I thought were the correct nodes, pieced together the fragments, and ended up with a URL.

I copied it into my browser and—nothing. Not that I was expecting anything since the URL looked what can best be termed odd.

This is where I paused and thought, Did I miss something? Maybe the new URL was also encrypted, just like the instructions URL?

I spiraled down a rabbit hole of testing encryption theories and trying out decryption methods. Was it Base64 again? AES? Something more obscure?

Nope. The problem wasn’t encryption. It was me.

Realizing the Missing Piece

After stepping away from the problem for a bit (pro tip: taking a break does help), I revisited the requirements and realized my mistake.

I’d overlooked part of the pattern identifying which nodes contained the valid fragments.

Tip 👀
Did I look things up on search engines and with AI? Yes, I did because why not be efficient. The ultimate goal is understanding a problem, figuring out where you went wrong. No search engine or AI will make you understand—they can only help—it’s all up to you.

In short, I wasn’t looking deep enough. The pattern wasn’t just a simple class name or attribute value—it required a more nuanced examination of the DOM structure.

The pieces started falling into place once I adjusted my logic to account for this missing detail.

This is one of those moments where you appreciate the beauty of debugging. Sure, it can be frustrating, but when you finally connect the dots and see where you went wrong, it’s like solving a mini-mystery.

The answer was laid out right in front of me but I mentally focused too much on the “challenge” aspect missing the obvious pieces. I shouldn’t be surprised; this happened with all those hidden object games too.

Victory: Extracting the Hidden Word

With the correct logic in place, I extracted the fragments, reconstructed the hidden URL, and hit enter in my browser. This time, it worked!

At the end of the URL was a single word—simple but satisfying. It felt like uncovering a secret message meant just for me. (Okay, technically it was for anyone doing the project, but let me have my moment.)

Leveling It Up with React

The final task was to display the hidden word on a webpage with a typewriter effect (no styles required).

Since I was working with React, I thought, Why not make it reusable and clean?

Here’s a simplified version of how I tackled it:

First, I created the Typewriter component that would take a list of letters as a data prop, loop over each letter, and display it at a timed interval.

export default function Typerwriter({data}) {
    const [display, setDisplay] = React.useState("");
		// Display data with typewriter effect
		React.useEffect(()=>{
	    // If no data, return
	    if(!data) return;
	
	    // Init index counter 
	    let index = 0;
	
	    // Render data items with interval of 500ms
	    const interval = setInterval(()=>{
	      // Update display
	      setDisplay((prevState) => prevState += data[index]);
	      // Increment index 
	      index++;
	      // Check if all items rendered and stop
	      if(index === data.length) clearInterval(interval);
	    }, 500);
	
	    // Clean up 
	    return () => {
	      clearInterval(interval);
	    }
	  }, [data]);

  return display;
}

The “animation” stems from the incremental reveal of each letter until there aren’t any letters left for the given word.

I use setInterval to render each item in the data list at an interval of 500 milliseconds (an arbitrary number since no specific value was requested). Since setInterval is a side effect, it goes into a useEffect hook to execute when the component mounts and when the data dependency changes (a very liberal assumption).

Get started: How To Make A Really Good Shopping Cart With React

Tip: Cleanup the interval to avoid unpredictable behavior caused by lingering intervals when the component re-renders.

Second, we’ll fetch the data from the URL and pass it to the Typewriter component. This involves doing the initial data fetch from the decoded URL and looks something like this:

function App() {
  const [data, setData] = React.useState([]);
  const [isLoading, setIsLoading] = React.useState(false);
  const [isError, setIsError] = React.useState(false);
  

  React.useEffect(()=>{
    // Get data on mount
    const getData = async () => {
      // Init loader
      setIsLoading(true);
      try {
        const data = await fetchUrl(URL);
        setData(strToArr(data));
      } catch(error) {
        setIsError(error.message);
      } finally {
        setIsLoading(false);
      }
    }

    getData();
  }, [URL]);

  if(isLoading) return <p>Loading...</p>;
  if(isError) return <p>Oops, something went wrong, check console</p>;

  return (
    <div>
      <Typewriter data={data} />
    </div>
  )
}

Related: How To Handle React Lists And Conditionals (Part 5)

The component’s logical flow initializes some state for the data we’ll fetch and for fetching indicators (isLoading and isError).

It was required to display loading text when the fetch was in progress so that’s a must. The error message may be a courtesy of life as a developer (you soon learn logging messages and errors can be one of the kindest things you do for yourself 🥲).

Note functions fetchUrl and strToArr—these are custom helper functions defined outside of the component. Since they’re defined outside and their references are constant (i.e. they don’t change between renders), they don’t need to be part of the useEffect dependency array.

// Helper fetch utility
async function fetchUrl(url) {
  try {
    const response = await fetch(url);
    if(!response.ok) throw new Error(`Something went wrong while fetching from URL: ${response.status}`);
    const data = await response.text();
    return data;
  } catch(err) {
    console.error(`Data fetch failed: ${err}`);
    throw (err);
  }
}

// Helper data transform
function strToArr(str) {
  const charList = str.split("");
  return charList;
}

Note: However, fetchUrl does depend on the mutable state of URL and so URL is part of the dependency array.

We’ll start the fetch by displaying the loading text and then request the data from the URL. If we get a successful response, we reformat the response data into the array of characters we need to pass onto Typewriter. If we encounter an error, we’ll throw an error and in either a success or failure state, we remove the loading screen.

Error handling—check ✅

And that’s pretty much the challenge for you.

Why Bother with a Challenge You Don’t Need?

If you’re wondering why I spent time on this without applying for the job, here’s the thing: it wasn’t about the job. It was about the challenge. (Okay, fine, maybe I did consider for a hot minute applying 👉 👈).

Related: How Building Something Useful Can Help You Become Better At Coding

As a software engineer, you’re constantly learning. Technologies evolve, frameworks change, and even the fundamentals can surprise you when applied in new ways.

Projects like this are a great way to sharpen your skills, think critically, and have some fun.

Here are a few things I gained from this project:

  1. Improved debugging skills: I was reminded (yet again) to slow down and read the requirements carefully. Honestly, best reminder of all time.
  2. Pattern recognition: The process of identifying the valid DOM nodes forced me to think about patterns in a structured way.
  3. Creative problem-solving: The typewriter effect was a small touch, but allowed me to play around with React, revisit the useEffect hook, and make something reusable. It’s always amazing how simple things humble you.

Capture the Flag for Coders

This project was reminiscent of the “capture the flag” game in cybersecurity. You’re hunting for clues, analyzing data, and piecing together a solution. It’s not always linear, and sometimes you get stuck—but that’s the point.

These types of challenges teach you how to think, not just how to code 🤓

Takeaways for Your Journey

I encourage you to take on challenges like this. You don’t need to wait for a job application or a formal assignment. Instead, find a problem that piques your curiosity and dive in!

Here are a few tips to make the most of it:

  1. Break it down: Focus on one piece of the problem at a time. First, I decoded the URL; second, I identified the DOM nodes; third, I rendered the data. Each step can also have sub-steps. Remember, breaking down the problem into a reasonable list of tasks will improve your reasoning and problem-solving.
  2. Embrace failure: Misunderstanding the directions was frustrating but also a crucial part of the learning process. Can obvious mistakes make you feel dumb? Yes, they can. But, again, they humble you and that’s not to be taken for granted.
  3. Document everything: Write down your thought process as you go. It helps you stay organized and gives you something to look back on later. Even better, outline your steps into cohesive notes in the editor. Your script doesn’t need to be the next great literature work, but it doesn’t hurt having a narrative.

Final Thoughts

Did I need to do this project? No. But am I glad I did? Absolutely.

Projects like these remind me why I love coding—it’s not just about building apps or writing scripts; it’s about solving problems and continually pushing the boundaries of my knowledge.

So, the next time you stumble across a challenge, even if it’s not “required,” consider giving it a shot. Who knows what you might learn—or how much fun you’ll have along the way.

Got any fun coding stories or challenges you’ve tackled? Drop them in the comments—I’d love to hear about them!

‘Till the next one, friends 👋

Related Posts