Use the master deck when you want a mixed review of the standalone tools material, then use the master quiz to practice choosing and applying the right command, language feature, or workflow.
Tools Master Flashcards
A comprehensive mix of the standalone tools flashcards: shell, regular expressions, programming-language essentials, Git, Java, C, and Make.
Difficulty:Basic
What does ls do?
Lists files and directories in the current (or specified) directory
ls (list) shows the contents of a directory. Common flags: -l for a detailed long listing, -a to include hidden files (those starting with .), and -lh for human-readable file sizes.
Difficulty:Basic
What does mkdir do?
Creates a new directory
mkdir (make directory) creates one or more directories. Use mkdir -p path/to/nested/dir to create all intermediate directories at once without errors if they already exist.
Difficulty:Basic
What does cp do?
Copies a file or directory to a new location
cp source dest copies a file. To copy a directory and all its contents recursively, use cp -r source/ dest/. The original file is left unchanged.
Difficulty:Basic
What does mv do?
Moves or renames a file or directory
mv old new renames a file if both paths are on the same filesystem, or moves it otherwise. Unlike cp, the original is removed — there is no -r flag needed for directories.
Difficulty:Basic
What does rm do?
Permanently deletes files or directories
rm file deletes a file. Use rm -r dir/ to delete a directory and all its contents recursively. There is no trash or undo — deleted files are gone immediately.
Difficulty:Intermediate
What does less do?
Displays file contents one screen at a time, with scrolling
less file opens an interactive pager. Navigate with arrow keys or Page Up/Down, search with /keyword, and quit with q. Unlike cat, it doesn’t dump everything to the terminal at once — essential for large files.
Difficulty:Basic
What does cat do?
Prints the contents of a file to standard output
cat (concatenate) reads one or more files and writes them to stdout. It’s commonly used to view small files, combine files (cat a b > c), or feed file contents into a pipeline.
Difficulty:Intermediate
What does sed do?
Edits a stream of text — most commonly used for find-and-replace
sed (stream editor) applies editing commands to each line of input. The most common usage is substitution: sed 's/old/new/g' replaces every occurrence of old with new. The g flag means global (all matches per line, not just the first).
Difficulty:Basic
What does grep do?
Searches text and prints lines that match a pattern
grep pattern file prints every line containing the pattern. Key flags: -i (case-insensitive), -r (recursive search through directories), -n (show line numbers), -v (invert — show lines that do NOT match).
Difficulty:Intermediate
What does head do?
Prints the first lines of a file (default: 10)
head file shows the first 10 lines. Use head -n 20 file to show the first 20 lines. Useful for quickly inspecting the start of a file or checking a CSV header.
Difficulty:Intermediate
What does tail do?
Prints the last lines of a file (default: 10)
tail file shows the last 10 lines. Use tail -n 20 for more. The -f flag (follow) continuously streams new lines as they are appended — the standard way to monitor a live log file.
Difficulty:Basic
What does wc do?
Counts lines, words, and bytes in a file
wc file prints line count, word count, and byte count. Use flags to isolate each: -l (lines), -w (words), -c (bytes). For example, wc -l access.log tells you how many requests are in a log file.
Difficulty:Basic
What does sort do?
Sorts lines of text alphabetically (or numerically)
sort file outputs lines in ascending alphabetical order. Key flags: -n (numeric sort), -r (reverse/descending), -u (unique — remove duplicate lines). Combine with pipes: cat file | sort -n | tail -5 finds the five largest numbers.
Difficulty:Intermediate
What does cut do?
Extracts specific columns or fields from each line of text
cut -d',' -f2 file.csv splits each line on , and prints the second field. Use -d to set the delimiter and -f to choose the field(s). Essential for quickly extracting a column from a CSV or colon-separated file like /etc/passwd.
Difficulty:Intermediate
What does ssh do?
Opens an encrypted remote shell session on another machine
ssh user@host connects to host as user over the SSH protocol. Everything — including your password and the session data — is encrypted. You can also use it to run a single remote command: ssh user@host 'df -h'.
Difficulty:Intermediate
What does htop do?
Shows an interactive, real-time view of running processes and system resource usage
htop is an improved alternative to top. It displays CPU, memory, and swap usage at the top, with a scrollable list of processes below. You can sort by CPU or memory, search for a process, and kill processes interactively — all with keyboard shortcuts.
Difficulty:Basic
What does pwd do?
Prints the absolute path of the current working directory
pwd (print working directory) tells you exactly where you are in the filesystem. Useful when you’re deep in a directory tree or when writing scripts where you need to capture the script’s launch location.
Difficulty:Intermediate
What does chmod do?
Changes the read, write, and execute permissions of a file or directory
chmod (change mode) controls who can read, write, or execute a file. chmod +x script.sh adds execute permission for everyone. You can also use numeric notation: chmod 755 file sets owner=rwx, group=rx, others=rx — a common pattern for executable scripts.
Difficulty:Basic
You need to see a list of all the files and folders in your current directory. What command do you use?
ls
The ls command lists directory contents. You can also use flags like ls -l for a detailed list or ls -a to see hidden files.
Difficulty:Basic
You are currently in your home directory and need to navigate into a folder named ‘Documents’. Which command achieves this?
cd Documents
The cd (change directory) command is used to change the current working directory in the command line operating system.
Difficulty:Basic
You want to quickly view the entire contents of a small text file named ‘config.txt’ printed directly to your terminal screen.
cat config.txt
The cat (concatenate) command reads data from the file and gives its content as output. It is widely used to display file contents.
Difficulty:Basic
You need to find every line containing the word ‘ERROR’ inside a massive log file called ‘server.log’.
grep 'ERROR' server.log
grep searches for patterns in each file. It prints each line that matches the given pattern, making it invaluable for parsing logs.
Difficulty:Intermediate
You wrote a new bash script named ‘script.sh’, but when you try to run it, you get a ‘Permission denied’ error. How do you make the file executable?
chmod +x script.sh
The chmod command changes file modes or Access Control Lists. The +x flag specifically adds execute permissions to the file.
Difficulty:Basic
You want to rename a file from ‘draft_v1.txt’ to ‘final_version.txt’ without creating a copy.
mv draft_v1.txt final_version.txt
The mv command is used to move files or directories, but it is also the standard command used to rename a file by moving it to a new name in the same directory.
Difficulty:Basic
You are starting a new project and need to create a brand new, empty folder named ‘src’ in your current location.
mkdir src
mkdir stands for ‘make directory’. It creates a new directory with the specified name, provided you have the correct permissions.
Difficulty:Intermediate
You want to view the contents of a very long text file called ‘manual.txt’ one page at a time so you can scroll through it.
less manual.txt
The less command is a terminal pager program that allows viewing (but not editing) the contents of a text file one screen at a time, allowing both forward and backward navigation.
Difficulty:Basic
You need to create an exact duplicate of a file named ‘report.pdf’ and save it as ‘report_backup.pdf’.
cp report.pdf report_backup.pdf
The cp (copy) command is used to copy files or directories from one location to another.
Difficulty:Basic
You have a temporary file called ‘temp_data.csv’ that you no longer need and want to permanently delete from your system.
rm temp_data.csv
The rm (remove) command deletes files or directories. Use with caution, as deleted files are generally not recoverable.
Difficulty:Basic
You want to quickly print the phrase ‘Hello World’ to the terminal or pass that string into a pipeline.
echo 'Hello World'
The echo command outputs the strings it is being passed as arguments. It is commonly used in scripts to display messages or write data to a file using redirection.
Difficulty:Basic
You want to know exactly how many lines are contained within a file named ‘essay.txt’.
wc -l essay.txt
The wc (word count) command prints newline, word, and byte counts for a file. Adding the -l flag restricts the output to only the line count.
Difficulty:Intermediate
You need to perform an automated find-and-replace operation on a stream of text to change the word ‘apple’ to ‘orange’.
sed 's/apple/orange/g'
In sed’s s/find/replace/ command, s means substitute and the / characters delimit the search and replacement terms. The trailing g (global) replaces every match on a line rather than just the first.
Difficulty:Intermediate
You want to store today’s date (formatted as YYYY-MM-DD) in a variable called TODAY so you can use it to name a backup file dynamically.
TODAY=$(date +%Y-%m-%d)
Command substitution $(...) executes the inner command in a subshell and replaces itself with the command’s standard output. This is the standard way to capture the result of a command into a variable.
Difficulty:Intermediate
A variable FILE holds the value my report.pdf. Running rm $FILE fails with a ‘No such file or directory’ error for both ‘my’ and ‘report.pdf’. How do you fix this?
rm "$FILE"
Without quotes, the shell performs word splitting on the expanded variable, breaking my report.pdf into two separate arguments: my and report.pdf. Wrapping the variable in double quotes ("$FILE") prevents word splitting and passes the entire value as a single argument.
Difficulty:Intermediate
You are writing a script that requires exactly two arguments. How do you check how many arguments were passed to the script so you can print a usage error if the count is wrong?
$#
$# expands to the total count of positional arguments passed to the script. A typical guard looks like: if [ $# -ne 2 ]; then echo 'Usage: script.sh src dest'; exit 1; fi. Other useful special variables: $1, $2 (individual args), $0 (script name), $@ (all args).
Difficulty:Intermediate
You want to create a directory called ‘build’ and then immediately run cmake .. inside it, but only if the directory creation succeeded — all in a single command.
mkdir build && cd build && cmake ..
The && (AND) operator runs the next command only if the previous one succeeded (exit code 0). If mkdir fails, neither cd nor cmake will execute. This is a concise alternative to a full if/then/fi block for simple success-guarded sequences.
Difficulty:Intermediate
At the start of a script, you need to change into /deploy/target. If that directory doesn’t exist, the script must abort immediately — write a defensive one-liner.
cd /deploy/target || exit 1
The || (OR) operator runs the right-hand command only if the left-hand command fails (non-zero exit code). This is the standard idiom for aborting a script when a critical precondition is not met, without needing a full if statement.
Difficulty:Intermediate
You want to delete all files ending in .tmp in the current directory using a single command, without listing each filename explicitly.
rm *.tmp
The * wildcard is a glob pattern — it is expanded by the shell itself (filename expansion) before rm runs. Globbing is entirely separate from regular expressions: in RegEx, * means ‘zero or more of the preceding character’, not ‘match anything’.
Difficulty:Intermediate
You want to count how many lines in server.log contain the word ‘ERROR’.
grep 'ERROR' server.log | wc -l
grep filters and prints only the matching lines; wc -l counts them. Without the pipe you would have to save a temp file and count it separately.
Difficulty:Intermediate
You have a file names.txt with one name per line. Print only the unique names, sorted alphabetically.
sort names.txt | uniq
uniq only removes adjacent duplicates, so sort must come first to bring duplicates together. Then uniq collapses consecutive identical lines into one.
Difficulty:Intermediate
You have a file names.txt with one name per line. Print each unique name alongside a count of how many times it appears.
sort names.txt | uniq -c
sort brings duplicates together so they are adjacent; uniq -c then collapses consecutive identical lines into one and prefixes each with its count.
Difficulty:Intermediate
List all running processes and show only those belonging to user tobias.
ps aux | grep tobias
ps aux prints every running process with its owner; piping to grep tobias filters to lines that contain that username.
Difficulty:Advanced
Print the 3rd line of config.txt without using sed or awk.
head -3 config.txt | tail -1
head -3 keeps only the first three lines; tail -1 then takes the very last of those — which is line 3 of the original file.
Difficulty:Intermediate
List the 5 largest files in the current directory, with the biggest first, showing only their names.
ls -S | head -5
ls -S sorts directory entries by file size (largest first); head -5 keeps only the first 5 lines of that output.
Difficulty:Advanced
You want to replace every occurrence of http:// with https:// in links.txt and save the result to links_secure.txt.
sed 's|http://|https://|g' links.txt > links_secure.txt
Here we redirect sed’s stdout to a file instead of piping it onward, but sed reads from a file argument, transforms the stream, and writes to stdout — which the shell redirects with >.
Difficulty:Advanced
Print only the unique error lines from access.log that contain the word ‘ERROR’, sorted alphabetically.
grep 'ERROR' access.log | sort | uniq
grep filters to matching lines; sort brings identical lines together; uniq collapses consecutive duplicates into one — a classic 3-step de-duplication pipeline.
Difficulty:Advanced
Count the total number of files (not directories) inside the current directory tree.
find . -type f | wc -l
find . -type f recursively lists every regular file, one per line; wc -l counts those lines.
Difficulty:Intermediate
Show the 10 most recently modified files in the current directory, newest first.
ls -t | head -10
ls -t sorts directory entries by modification time (newest first); head -10 keeps only the first 10 lines of that output.
Difficulty:Advanced
Extract the second column from comma-separated data.csv, sort the values, and print only the unique ones.
cut -d',' -f2 data.csv | sort | uniq
cut -d',' splits each line on commas; -f2 keeps the second field; sort brings duplicates together; uniq removes consecutive duplicates.
Difficulty:Advanced
Convert the contents of readme.txt to uppercase and save the result to readme_upper.txt.
cat reads the file and feeds it into the pipeline; tr 'a-z' 'A-Z' translates every lowercase letter to uppercase; > redirects the final output to a new file.
Difficulty:Intermediate
Print every line from app.log that does NOT contain the word ‘DEBUG’.
grep -v 'DEBUG' app.log
The -v flag inverts the match — grep prints every line that does not match the pattern. No pipe is needed here, but grep -v ... | ... is a common pipeline building block.
Difficulty:Advanced
You have two files, file1.txt and file2.txt. Print all lines from both files that contain the word ‘success’, sorted alphabetically with duplicates removed.
grep can take multiple file arguments and searches all of them. By default it prefixes each match with the filename (file1.txt:success!), which would defeat uniq — identical match lines from different files would compare as different strings. The -h flag suppresses the filename prefix so sort | uniq can dedupe by content.
Difficulty:Basic
What metacharacter asserts the start of a string?
^ (Caret)
Placed at the very beginning of a pattern, it constrains the match to the start of the string.
Difficulty:Basic
What metacharacter asserts the end of a string?
$ (Dollar sign)
Placed at the very end of a pattern, it constrains the match to the end of the string.
Difficulty:Basic
What syntax is used to define a Character Class (matching any single character from a specified group)?
[...] (Square brackets)
Wrapping characters in square brackets tells the engine to match exactly one of the characters found inside.
Difficulty:Intermediate
What syntax is used inside a character class to act as a negation operator (matching any character NOT in the group)?
[^...] (Caret immediately after the opening bracket)
If the caret is the very first character inside the brackets, it inverts the character class.
Difficulty:Basic
What metacharacter is used to match any single digit?
\d
In ASCII-only engines (POSIX, JavaScript without the u flag), \d is equivalent to [0-9]. In Unicode-aware engines like Python 3 (the default mode), \d matches any Unicode digit — including non-ASCII digits like Devanagari ९ — so use [0-9] explicitly when you only want ASCII digits.
Difficulty:Basic
What meta character is used to match any ‘word’ character (alphanumeric plus underscore)?
\w
This is the direct equivalent of the character class [a-zA-Z0-9_].
Difficulty:Basic
What meta character is used to match any whitespace character (spaces, tabs, line breaks)?
\s
This quickly targets formatting and spacing characters.
Difficulty:Basic
What metacharacter acts as a wildcard, matching any single character except a newline?
. (Dot)
To match a literal dot, you would need to escape it (.).
Difficulty:Basic
What quantifier specifies that the preceding element should match ‘0 or more’ times?
* (Asterisk)
This allows the preceding character or group to be completely absent or repeat infinitely.
Difficulty:Basic
What quantifier specifies that the preceding element should match ‘1 or more’ times?
+ (Plus sign)
This guarantees the character or group appears at least once, but allows it to repeat infinitely.
Difficulty:Basic
What quantifier specifies that the preceding element should match ‘0 or 1’ time?
? (Question mark)
This essentially makes the preceding character, class, or group optional.
Difficulty:Intermediate
What syntax is used to specify that the preceding element must repeat exactly n times?
{n} (Curly braces with a number)
You can also specify a range of repetitions using {n,m}.
Difficulty:Basic
What syntax is used to create a group?
(...) (Parentheses)
This groups tokens together as a single unit. You can apply quantifiers to the whole group and access the matched substring by index (e.g., match[1]).
Difficulty:Advanced
What is the syntax used to create a Named Group?
(?<name>pattern) (or (?P<name>pattern) in Python)
A named group lets you assign a meaningful label to the match, so you can access it by name (e.g., match.groups.name) instead of just a numbered index.
Difficulty:Advanced
Write a regex to validate a standard email address (e.g., user@domain.com).
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
This matches a sequence of alphanumeric characters and allowed symbols for the username, followed by an ‘@’, a domain name, and a 2+ character top-level domain.
Difficulty:Expert
Write a regex to match a standard US phone number, with optional parentheses and various separators (e.g., 123-456-7890 or (123) 456-7890).
^(\(\d{3}\)|\d{3})[- .]?\d{3}[- .]?\d{4}$
The first group matches either 3 digits in parentheses or just 3 digits. The [- .]? allows for an optional dash, space, or dot between the number segments.
Difficulty:Advanced
Write a regex to match a 3 or 6 digit hex color code starting with a hashtag (e.g., #FFF or #1A2B3C).
^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$
Matches the literal ‘#’ followed by either exactly 6 hexadecimal characters or exactly 3 hexadecimal characters, ensuring no extra characters exist using the $ anchor.
Difficulty:Expert
Write a regex to validate a strong password (at least 8 characters, containing at least one uppercase letter, one lowercase letter, and one number).
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$
Uses positive lookaheads (?=...) to scan ahead and guarantee the presence of a lowercase, uppercase, and digit before matching a string of 8 or more valid characters.
Difficulty:Expert
Write a regex to match a valid IPv4 address (e.g., 192.168.1.1).
Ensures that each of the 4 octets falls between 0 and 255, separating the first three with literal dots.
Difficulty:Advanced
Write a regex to extract the domain name from a URL, ignoring the protocol and ‘www’ (e.g., extracting ‘example.com’ from ‘https://www.example.com/page’).
^(https?:\/\/)?(www\.)?(?<domain>[^\/]+)
The groups (...) make the ‘http(s)://’ and ‘www.’ optional. The named group (?<domain>...) matches all characters up to the first forward slash, accessible via match.groups.domain.
Difficulty:Advanced
Write a regex to match a date in the format YYYY-MM-DD with basic month and day validation.
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
Matches exactly 4 digits for the year, restricts the month to 01-12, and restricts the day to 01-31. Note: this does not validate that the day is correct for the specific month (e.g., it would accept February 31).
Difficulty:Advanced
Write a regex to match a time in 24-hour format (HH:MM).
^([01]\d|2[0-3]):([0-5]\d)$
The hour group allows either 00-19 or 20-23. The minute group strictly allows 00-59.
Difficulty:Advanced
Write a regex to match an opening or closing HTML tag.
<\/?[a-zA-Z][\s\S]*>
Matches the opening <, an optional / for closing tags, a starting letter (upper or lowercase), and then any characters (including newlines) until the closing >.
Difficulty:Intermediate
Write a regex to find all leading and trailing whitespaces in a string (commonly used for string trimming).
^\s+|\s+$
Matches one or more whitespace characters \s+ at the start ^ of the string, OR | one or more whitespace characters at the end $ of the string.
Difficulty:Basic
You are shown Python code. Explain what it does and what it returns or prints.
Prints Score: 95, GPA: 3.8 — the f"..." is an f-string that embeds variables inside {}. The :.1f format specifier rounds the float to 1 decimal place.
Difficulty:Basic
You are shown Python code. Explain what it does and what it returns or prints.
7/27//2
7 / 2 → 3.5 (float division — always returns a float in Python 3). 7 // 2 → 3 (floor/integer division — like C++’s / for ints).
Difficulty:Basic
You are shown Python code. Explain what it does and what it returns or prints.
x="5"+3
Raises TypeError: can only concatenate str to str. Python is strongly typed — it refuses to implicitly convert between str and int. Fix: int("5") + 3 → 8 or "5" + str(3) → "53".
Difficulty:Basic
You are shown Python code. Explain what it does and what it returns or prints.
squares=[x**2forxinrange(1,6)]
Produces [1, 4, 9, 16, 25] — a list comprehension that squares each number from 1 to 5. range(1, 6) is exclusive of the stop value. ** is the exponentiation operator.
Difficulty:Basic
You are shown Python code. Explain what it does and what it returns or prints.
nums=[4,8,15,16,23,42]big=[xforxinnumsifx>20]
Produces [23, 42] — a list comprehension with a filter condition. Only elements where x > 20 are included.
Difficulty:Basic
You are shown Python code. Explain what it does and what it returns or prints.
Opens data.txt, reads it line by line, and prints each line with leading/trailing whitespace removed. The with statement is a context manager that automatically closes the file when the block exits, even if an exception occurs.
Difficulty:Basic
You are shown Python code. Explain what it does and what it returns or prints.
Prints 0: apple, 1: banana, 2: cherry. enumerate() yields (index, value) pairs — the Pythonic way to get both index and element without using range(len(...)).
Difficulty:Intermediate
You are shown Python code. Explain what it does and what it returns or prints.
importrecodes=re.findall(r'\d+',"Error 404 and 500")
Returns ['404', '500'] — a list of strings matching all non-overlapping occurrences of one or more digits (\d+). The r'...' is a raw string that preserves backslashes for the regex engine.
Difficulty:Advanced
You are shown Python code. Explain what it does and what it returns or prints.
Replaces all IPv4 addresses in text with 'x.x.x.x'. re.sub(pattern, replacement, string) is the Python equivalent of sed 's/old/new/g'.
Difficulty:Intermediate
You are shown Python code. Explain what it does and what it returns or prints.
importsysprint("Error: file not found",file=sys.stderr)sys.exit(1)
Prints the error message to stderr (not stdout), then exits with code 1 (failure). file=sys.stderr is like C++’s std::cerr. sys.exit(1) is like C’s exit(1).
Difficulty:Intermediate
You are shown Python code. Explain what it does and what it returns or prints.
2**82^8
2 ** 8 → 256 (exponentiation — two to the eighth power). 2 ^ 8 → 10 (bitwise XOR — NOT exponentiation!). This is a common mistake from math notation.
Difficulty:Intermediate
You are shown Python code. Explain what it does and what it returns or prints.
importsysfilename=sys.argv[1]
Gets the first command-line argument. sys.argv[0] is the script name itself (like C’s argv[0]). If no argument is provided, this raises an IndexError.
Difficulty:Basic
Print a formatted string that says Student: Alice, GPA: 3.82 using a variable name = "Alice" and gpa = 3.82. Format the GPA to 2 decimal places.
print(f"Student: {name}, GPA: {gpa:.2f}")
f-strings use the f"..." prefix. {variable} embeds a value. :.2f formats to 2 decimal places.
Difficulty:Basic
Perform integer (floor) division of 7 by 2, getting 3 as the result (not 3.5).
7 // 2
Python’s / always returns a float. Use // for integer/floor division, which behaves like C++’s / for integers.
Difficulty:Basic
Compute 2 to the power of 10 (should give 1024).
2 ** 10
Python uses ** for exponentiation. Do NOT use ^ — that is bitwise XOR in Python.
Difficulty:Basic
Create a list of the squares of numbers 1 through 5: [1, 4, 9, 16, 25] using a single line of Python.
[x**2 for x in range(1, 6)]
A list comprehension: [expression for variable in iterable]. range(1, 6) generates 1, 2, 3, 4, 5 (stop is exclusive).
Difficulty:Basic
From a list nums = [4, 8, 15, 16, 23, 42], create a new list containing only the numbers greater than 20.
[x for x in nums if x > 20]
A list comprehension with a filter: [expression for var in iterable if condition].
Difficulty:Intermediate
Read a file called data.txt line by line, safely closing it even if an error occurs.
enumerate() yields (index, value) pairs. This is the Pythonic alternative to for i in range(len(fruits)).
Difficulty:Intermediate
Find all numbers (sequences of digits) in the string "Error 404 and 500" using regex.
importrecodes=re.findall(r'\d+',"Error 404 and 500")
re.findall() returns a list of all matches. \d+ matches one or more digits. Use raw strings r'...' for regex patterns.
Difficulty:Advanced
Replace all IP addresses in a string text with "x.x.x.x" using regex.
re.sub(r'\d+\.\d+\.\d+\.\d+', 'x.x.x.x', text)
re.sub(pattern, replacement, string) replaces all matches. This is the Python equivalent of sed 's/old/new/g'.
Difficulty:Advanced
Write a script that prints an error to stderr and exits with code 1 if no command-line argument is provided.
importsysiflen(sys.argv)<2:print("Error: no filename",file=sys.stderr)sys.exit(1)
sys.argv[0] is the script name; real args start at index 1. file=sys.stderr sends output to stderr. sys.exit(1) exits with a non-zero code.
Difficulty:Basic
Check the type of a variable x at runtime and print it.
print(type(x))
type() returns the type of any object. Python is dynamically typed — types are checked at runtime, not compile time.
Difficulty:Intermediate
Check if a regex pattern matches anywhere in a string line, returning True or False.
ifre.search(r'pattern',line):print("Found!")
re.search() returns a match object (truthy) or None (falsy). It is the Python equivalent of grep -q.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
letcount=0;constMAX=200;
let declares a mutable variable (can be reassigned). const declares an immutable binding (cannot be reassigned). Never use var — it has hoisting and scoping bugs.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
console.log(1=="1");console.log(1==="1");
First: true — == triggers implicit type coercion, converting "1" to 1 before comparing. Second: false — === checks both value AND type with no coercion. Always use ===.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
constname="Alice";console.log(`Hello, ${name}!`);
Prints Hello, Alice! — template literals use backticks (`) and ${expression} for interpolation. This is the JavaScript equivalent of Python’s f-strings.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
constdouble=n=>n*2;
Declares an arrow function that takes one parameter n and returns n * 2. Arrow functions are the modern, concise syntax for anonymous functions.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
Produces [2, 4] — .filter() creates a new array containing only elements where the callback returns true. The arrow function is a callback passed as an argument.
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
constsum=[1,2,3].reduce((acc,n)=>acc+n,0);
Produces 6 — .reduce() accumulates a single value by calling the callback for each element. acc starts at 0 (the second argument) and each step adds n to it.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
const{name,grade}={name:"Alice",grade:95};
Object destructuring — extracts the name and grade properties into separate variables: name = "Alice", grade = 95. Equivalent to const name = obj.name; const grade = obj.grade;.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
const[lat,lng]=[40.7,-74.0];
Array destructuring — assigns items by position: lat = 40.7, lng = -74.0. This is the JavaScript equivalent of Python’s tuple unpacking lat, lng = (40.7, -74.0).
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
Output: A, C, B — NOT A, B, C. The setTimeout callback goes to the Task Queue and only runs when the Call Stack is empty. Synchronous code always completes first, even with a 0ms delay.
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
An async function that fetches data from an API. await suspends this function (non-blocking — the Event Loop can do other work) until the Promise from fetch() resolves. result.json() also returns a Promise.
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
const[a,b]=awaitPromise.all([fetchA(),fetchB()]);
Runs fetchA() and fetchB()in parallel and waits for both to finish. Total time = max(A, B), not A + B. Uses array destructuring to assign results.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
constdoubled=[1,2,3].map(n=>n*2);
Produces [2, 4, 6] — .map() transforms each element by calling the callback and returns a new array of results. The original array is unchanged.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
console.log("Hello from Node.js!");
console.log() is JavaScript’s equivalent of Python’s print() and C++’s printf(). It writes to stdout with a trailing newline. It can print any value — strings, numbers, objects, arrays.
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
Creates a Promise manually. The constructor takes a function with two callbacks: resolve(value) — call when work succeeds, reject(error) — call when it fails. In practice you rarely create Promises from scratch; you mostly consume them with await or .then().
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
result is a Promise, not 42. Every async function always returns a Promise, even when the body just returns a plain value. To get 42, you need await getCount() or getCount().then(n => ...).
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
Optional chaining?. safely accesses nested properties — returns undefined instead of throwing if any link is null/undefined. Nullish coalescing?? provides a default only for null/undefined (unlike ||, which also replaces 0, '', and false).
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
letx;console.log(x);lety=null;console.log(y);
First prints undefined — a declared variable with no value assigned is undefined. Second prints null — explicitly set to ‘no value’. JavaScript has two ‘nothings’: undefined (missing) and null (intentionally empty). typeof undefined is "undefined", but typeof null is "object" (a famous JS bug).
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
Prints Alice then 95. Dot notation (student.name) and bracket notation (student["grade"]) both access object properties. Bracket notation is useful when the key is a variable: const key = "grade"; student[key].
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
JSON.stringify(obj) converts the object to the string '{"name":"Bob","grade":42}'. JSON.parse(json) converts that string back to an object. JSON is the standard format for sending data over HTTP — res.json() in Express calls JSON.stringify for you.
Difficulty:Intermediate
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
found is { id: 2, name: "Bob" }. .find() returns the first element where the callback returns true, or undefined if no match. Unlike .filter() which returns an array, .find() returns a single element.
Difficulty:Basic
You are shown JavaScript/Node.js code. Explain what it does and what it outputs.
if (score>=90){console.log("A");}elseif (score>=60){console.log("Pass");}else{console.log("Fail");}
JavaScript control flow uses C++-style braces {} (not Python’s colon + indentation). There is no elif — use else if. Braces are required for multi-line blocks.
Difficulty:Basic
Declare a mutable variable count set to 0 and an immutable constant MAX set to 200.
letcount=0;constMAX=200;
Use let for mutable variables and const for constants. Never use var — it has hoisting/scoping issues.
Difficulty:Intermediate
Check if a variable userInput (which might be a string) equals the number 42, without being tricked by type coercion.
userInput === 42 or Number(userInput) === 42
Always use === (strict equality). == would coerce "42" to 42, silently masking a type mismatch.
Difficulty:Basic
Create a string that says Hello, Alice! Score: 95 using variables name = "Alice" and score = 95, with interpolation.
`Hello, ${name}! Score: ${score}`
Template literals use backticks and ${expression}. This is the JS equivalent of Python’s f-strings. Single/double quotes do NOT support interpolation.
Difficulty:Basic
Write an arrow function add that takes two parameters and returns their sum.
const add = (a, b) => a + b;
Arrow functions use =>. For a single expression, the return keyword and braces can be omitted.
Difficulty:Basic
Given const nums = [1, 2, 3, 4, 5], create a new array containing only the even numbers using a higher-order function.
const evens = nums.filter(n => n % 2 === 0);
.filter() takes a callback and returns a new array with only elements where the callback returns true.
Difficulty:Basic
Given const nums = [1, 2, 3], create a new array where each number is doubled.
const doubled = nums.map(n => n * 2);
.map() transforms each element by applying the callback and returns a new array. The original is unchanged.
Difficulty:Intermediate
Compute the sum of [1, 2, 3, 4, 5] using a single expression.
[1, 2, 3, 4, 5].reduce((acc, n) => acc + n, 0)
.reduce() accumulates a value. The second argument (0) is the initial accumulator value.
Difficulty:Basic
Extract name and grade from const student = { name: "Alice", grade: 95 } into separate variables in one line.
const { name, grade } = student;
Object destructuring extracts named properties. This pattern is used in every React component to destructure props.
Difficulty:Intermediate
Schedule a function to run after the current call stack empties (with minimal delay).
setTimeout(() => { /* code */ }, 0);
setTimeout(fn, 0) does NOT run immediately — it queues fn in the Task Queue. The Event Loop only dequeues it when the Call Stack is completely empty.
Difficulty:Advanced
Write an async function loadUser that fetches user data from /api/user, handles errors, and logs the result.
new Promise(resolve => ...) creates a Promise. Passing resolve as the setTimeout callback means: ‘resolve this Promise after ms ms.’ Usage: await delay(1000) pauses for 1 second without blocking the Event Loop.
Difficulty:Advanced
Safely read response.data.user.name where any part of the chain might be null or undefined. Fall back to 'Anonymous' if missing.
?. short-circuits to undefined if any link is null/undefined — no TypeError thrown. ?? uses the fallback only for null/undefined, so a real empty string '' would be preserved. Using || instead of ?? would incorrectly replace an empty string with 'Anonymous'.
Difficulty:Basic
Create a JavaScript object with properties name (“Alice”) and grade (95), then convert it to a JSON string.
Object literals use { key: value } syntax. JSON.stringify() converts to a JSON string for sending over HTTP. JSON.parse() does the reverse.
Difficulty:Intermediate
Given const students = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }], find the student with id === 2 (return the object, not an array).
const student = students.find(s => s.id === 2);
.find() returns the first matching element (or undefined). .filter() would return an array [{ id: 2, name: 'Bob' }] — one element, but still wrapped in an array.
Difficulty:Basic
Declare a variable with no initial value. What is its value? Then set a different variable explicitly to ‘nothing’.
letx;// x is undefinedlety=null;// y is null (intentionally empty)
undefined means ‘no value assigned yet’. null means ‘intentionally empty’. JavaScript has both — unlike Python which only has None.
Difficulty:Basic
Write a for...of loop that iterates over const names = ['Alice', 'Bob', 'Carol'] and logs each name.
for (constnameofnames){console.log(name);}
for...of is JavaScript’s equivalent of Python’s for name in names. Use const (not let) since the loop variable isn’t reassigned within the body.
Difficulty:Basic
You are shown React/JSX code. Explain what it does and what it renders.
A React component — a function that returns JSX. It renders an <h1> with blue text. The double braces in style={{...}} are: outer {} = JSX expression, inner {} = JavaScript object literal.
Difficulty:Basic
You are shown React/JSX code. Explain what it does and what it renders.
<ProductCardname="Laptop"price={999.99}/>
Renders the ProductCard component with two props: name (string "Laptop") and price (number 999.99). String props use quotes; all other types use {}.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
A composable container component. title is a regular prop. children is a special prop containing whatever JSX is nested between <Card> and </Card>. This enables composition over inheritance.
Difficulty:Basic
You are shown React/JSX code. Explain what it does and what it renders.
const[count,setCount]=React.useState(0);
Declares a state variablecount with initial value 0 and a setter functionsetCount. Calling setCount(newValue) triggers a re-render. The array destructuring extracts the pair returned by useState.
Difficulty:Basic
You are shown React/JSX code. Explain what it does and what it renders.
A button with a click event handler. When clicked, it calls setCount(count + 1) which updates state and triggers a re-render. Note: onClick (camelCase), not onclick.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
Renders an array of <li> elements from the tasks data array using .map(). Each element has a stable key prop (task.id) that helps React’s reconciler track which items changed.
Difficulty:Basic
You are shown React/JSX code. Explain what it does and what it renders.
{isLoggedIn?<Dashboard/>:<LoginForm/>}
Conditional rendering using a ternary — renders Dashboard if isLoggedIn is true, LoginForm otherwise. This is one of two standard patterns (the other is &&).
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
{unreadCount>0&&<Badgecount={unreadCount}/>}
Short-circuit conditional rendering — renders Badge ONLY when unreadCount > 0. When the left side is false, React renders nothing. Note: use > 0, not just unreadCount &&, to avoid rendering 0 as text.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
setItems([...items,newItem]);
Correctly adds newItem to state. Creates a new array with the spread operator (...items copies all existing items, then newItem is appended). You must create a new array — items.push(newItem) mutates in-place and React won’t detect the change.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
<SearchBarvalue={text}onChange={setText}/>
Passes the state value text as a prop and the setter setText as a callback prop. This is inverse data flow — the child calls onChange(newValue) to notify the parent, which updates state and triggers re-render of both.
Difficulty:Basic
You are shown React/JSX code. Explain what it does and what it renders.
<imgsrc={url}alt="logo"/>
A self-closing JSX tag for an image. In JSX, self-closing tags must include the /> (unlike HTML where <img> is valid). src={url} passes a JS variable; alt="logo" passes a string literal.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
A reusable component that renders a colored badge. It destructures label and color from props. The style prop takes a JS object with camelCase properties (borderRadius, not border-radius).
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
useEffect(()=>{document.title='Hello!';},[]);
Runs a side effect once after the component first mounts. The empty array [] is the dependency array — it tells React ‘don’t re-run this when anything changes’. Common uses: fetching initial data, setting up subscriptions, updating document.title.
Difficulty:Advanced
You are shown React/JSX code. Explain what it does and what it renders.
Fetches user data on mount and re-fetches whenever userId changes. userId in the dependency array tells React: re-run this effect when userId takes a new value. Without listing userId, the effect would keep showing the first user’s data even after the prop changes — a stale closure bug.
Difficulty:Advanced
You are shown React/JSX code. Explain what it does and what it renders.
setCount(prev=>prev+1);
The functional update form of setState. Instead of passing the next value directly, you pass a function. React calls that function with the guaranteed latest state value as prev. Use this whenever new state depends on old state — it prevents stale closure bugs where a batch of rapid updates could all read the same outdated value.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
setItems(items.filter(item=>item.id!==targetId));
Removes the item with id === targetId from the items state array. .filter() returns a new array (never mutates the original), which is required — React detects changes by reference. All items where the condition is true are kept; the one matching targetId is excluded.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
setUser({...user,name:'Bob'});
Updates the name field of the user state object while preserving all other fields. The spread ...user copies every existing key into a new object, then name: 'Bob' overrides just that one key. Never mutate the object directly (user.name = 'Bob') — that changes the same reference React already has, so it won’t detect the change and won’t re-render.
Difficulty:Intermediate
You are shown React/JSX code. Explain what it does and what it renders.
A controlled input — React owns the input’s value by binding it to the query state variable. Every keystroke fires onChange, which calls setQuery, which updates state, which causes React to re-render the input with the new value. This makes React the single source of truth for the field’s content, so you can read or validate the value at any time from query.
Difficulty:Basic
Write a React component Greeting that renders an <h1> saying Hello, Alice! using a variable name.
JSX style takes a JS object (not a CSS string). Double braces: outer = JSX expression, inner = object literal. CSS properties use camelCase.
Difficulty:Advanced
Write a component ProductCard that accepts name, price, and onSale props. Show the name in an <h3>, the price formatted to 2 decimals, and a ‘Sale!’ span only when onSale is true.
This is lifting state up — state lives in the parent, child notifies parent via callback prop. This is the standard React pattern for two-way data binding.
Difficulty:Basic
Use className (not class) to apply the CSS class app-title to an <h1> element in JSX.
<h1 className="app-title">My App</h1>
class is a reserved JavaScript keyword (for ES6 classes). JSX uses className instead, which maps to the DOM property element.className.
Difficulty:Advanced
Write a useEffect that calls fetchPosts() once when a component mounts, storing the result in a posts state variable. Assume fetchPosts() returns a Promise that resolves to an array.
The empty dependency array [] means ‘run once after the initial render, then never again’ — exactly what ‘on mount’ means. useState([]) initializes with an empty array so the list renders safely before data arrives.
Difficulty:Advanced
Write a counter that increments correctly even if the button is clicked many times rapidly. Use the functional update pattern.
prev => prev + 1 asks React for the guaranteed latest value at update time, preventing stale reads if multiple events are batched. Note onClick={handleClick} — passing the function reference, not calling it (handleClick() would trigger on every render).
Difficulty:Intermediate
Remove the item with id === deletedId from the tasks state array.
.filter() returns a new array containing only items where the condition is true — the one with the matching id is excluded. Never use .splice() or index-based deletion on state arrays; they mutate in-place and React won’t detect the change.
Difficulty:Intermediate
Update the score field of the player state object to newScore, keeping all other fields unchanged.
setPlayer({ ...player, score: newScore });
Spread ...player copies all existing fields into a new object, then score: newScore overrides just that one field. The result is a new object reference, which signals React to re-render. Mutating directly (player.score = newScore) would leave the same reference and be invisible to React.
Difficulty:Basic
Render an <h2> and a <p> side by side as siblings without adding a wrapper <div> to the DOM.
return (<><h2>Title</h2><p>Subtitle</p></>);
<>...</> is a React Fragment — a wrapper that exists only in JSX, not in the real DOM. Use it whenever a component must return multiple elements but you don’t want an extra <div> cluttering the HTML structure or breaking CSS layouts like flexbox and grid.
Difficulty:Advanced
Write a controlled text input that is bound to a username state variable. Every keystroke should update the state.
A controlled input has two parts: value={username} binds the displayed text to state, and onChange updates state on every keystroke via e.target.value. React becomes the single source of truth for the input — you can read or validate username at any time without touching the DOM.
Difficulty:Intermediate
You have some uncommitted, incomplete changes in your working directory, but you need to switch to another branch to urgently fix a bug. How do you temporarily save your current work without making a messy commit?
git stash
git stash temporarily shelves your staged and unstaged changes for tracked files. To include brand new, untracked files in the stash, you must use git stash -u.
Difficulty:Intermediate
You know a bug was introduced recently, but you aren’t sure which commit caused it. How do you perform a binary search through your commit history to find the exact commit that broke the code?
git bisect
git bisect systematically halves your commit history, asking you to test if the bug is present at each step, making it highly efficient for tracking down regressions.
Difficulty:Intermediate
You are looking at a file and want to know exactly who last modified a specific line of code, and in which commit they did it.
git blame
git blame annotates each line of a file with the author and the commit hash of the last modification, which is very useful for figuring out who to ask about a piece of code.
Difficulty:Intermediate
You want to safely ‘undo’ a previous commit that introduced an error, but you don’t want to rewrite history or force-push. How do you create a new commit with the exact inverse changes?
git revert
git revert is the safest way to undo changes on a shared branch because it adds a new commit that nullifies the targeted commit, rather than deleting history.
Difficulty:Intermediate
You want to see exactly what has changed in your working directory compared to your last saved snapshot (the most recent commit).
git diff HEAD
Using git diff HEAD compares your current modified tracked files on disk with the most recent commit. Note that it does not show completely untracked files.
Difficulty:Advanced
You have a feature branch with several experimental commits, but you only want to move one specific, completed commit over to your main branch.
git cherry-pick
git cherry-pick allows you to selectively grab a specific commit from one branch and apply it onto your current branch, without merging the entire branch history.
Difficulty:Advanced
You want to integrate a feature branch into main, but instead of bringing over all 15 tiny incremental commits, you want them combined into one clean commit on the main branch.
git merge --squash
Squashing collapses every patch from the feature branch into one set of changes, so a single clean commit lands on the target branch. It only stages those changes, though — you must follow it with git commit to materialise the squashed commit.
Difficulty:Advanced
You are building a massive project and want to include an entirely separate external Git repository as a subdirectory within your project, while keeping its history independent.
git submodule add <url> <path>
git submodule add clones the external repo into <path> and records a gitlink (mode 160000) pointing at its current commit SHA, plus a .gitmodules entry with the URL. The outer repo stores just that pinned SHA — no file duplication. Teammates get the content via git clone --recursive (or git submodule update --init after a plain clone).
Difficulty:Basic
You are starting a brand new project in an empty folder on your computer and want Git to start tracking changes in this directory.
git init
Initializes a new, empty Git repository in the current directory, creating the hidden .git folder that holds all version-control data. It is a one-time setup step per project.
Difficulty:Basic
You have just installed Git on a new computer and need to set up your username and email address so that your commits are properly attributed to you.
git config
Using git config --global user.name and user.email establishes your default identity, which can be overridden for specific projects using git config --local.
Difficulty:Basic
You’ve made changes to three different files, but you only want two of them to be included in your next snapshot. How do you move those specific files to the staging area?
git add <filename>
The git add command moves file modifications from the working directory into the staging area (index) to prepare them for the next commit.
Difficulty:Basic
You’ve lost track of what you’ve been doing. You want a quick overview of which files are modified, which are staged, and which are completely untracked by Git.
git status
Summarizes the working tree and staging area — what is modified, staged, or untracked — without changing anything. It is the cheapest way to orient yourself before staging or committing.
Difficulty:Basic
You have staged all the files for a completed feature and are ready to permanently save this snapshot to your local repository’s history with a descriptive message.
git commit -m 'message'
Records everything currently in the staging area as a new, immutable snapshot in your local history. Only staged changes are included — unstaged edits stay in the working directory.
Difficulty:Basic
You want to review the chronological history of all past commits on your current branch, including their author, date, and commit message.
git log
This command git log prints out the commit history. You can also add flags like -p to see the exact patch/diff introduced in each commit.
Difficulty:Intermediate
You’ve made edits to a file but haven’t staged it yet. You want to see the exact lines of code you added or removed compared to what is currently in the staging area.
git diff
Running git diff without any arguments compares your current working directory against the staging area.
Difficulty:Basic
You want to create a new branch pointer for a future feature without switching branches yet. Which command creates that branch at your current commit?
git branch <branch-name>
This command git branch creates a new branch pointer at your current commit, allowing you to diverge from the main line of development.
Difficulty:Basic
You are currently on your feature branch and need to switch your working directory back to the ‘main’ branch.
git switch main
The modern git switch command navigates between branches, updating your working directory and moving HEAD. The legacy equivalent git checkout main still works and is widely seen in older documentation and scripts.
Difficulty:Intermediate
Your feature branch is complete, and you want to integrate its entire commit history into your current ‘main’ branch.
git merge <branch-name>
Merging with git merge takes the divergent histories of two branches and combines them, often resulting in a new ‘merge commit’ with two parents.
Difficulty:Advanced
Instead of creating a merge commit, you want to take the commits from your feature branch and re-apply them directly on top of the latest ‘main’ branch to create a clean, linear history.
git rebase main
Rebasing with git rebase rewrites history by moving the base of your current branch to the tip of another, which creates a cleaner history but should be avoided on public/shared branches.
Difficulty:Basic
You want to start working on an open-source project hosted on GitHub. How do you download a full local copy of that repository to your machine?
git clone <url>
Cloning with git clone downloads the entire repository, including its full history and all branches, from a remote server to your local disk.
Difficulty:Intermediate
Your team members have uploaded new commits to the shared remote repository. You want to fetch those changes and immediately integrate them into your current local branch.
git pull
The git pull command is effectively a combination of git fetch and git merge (the default) or git rebase (if configured), integrating remote changes into your local branch.
Difficulty:Basic
You have finished making several commits locally and want to upload them to the remote GitHub repository so your team can see them.
git push
git push transmits your local commits to the remote server, updating the remote branch to match your local branch.
Difficulty:Intermediate
You have a specific commit hash and want to see detailed information about it, including the commit message, author, and the exact code diff it introduced.
git show <commit-hash>
git show displays the details of a specific Git object, most commonly used to inspect exactly what changed in a single commit.
Difficulty:Intermediate
You want to start working on a new feature in isolation. How do you create a new branch called ‘feature-auth’ and immediately switch to it in a single command?
git switch -c feature-auth
git switch -c creates a new branch and switches to it in one step. The -c flag stands for ‘create’. The legacy equivalent is git checkout -b feature-auth, which you will still encounter in older scripts and documentation.
Difficulty:Intermediate
You accidentally staged a file you didn’t intend to include in your next commit. How do you move it back to the working directory without losing your modifications?
git restore --staged <filename>
git restore --staged unstages a file by copying the version from the last commit back into the staging area, leaving your working directory modifications completely untouched. It is the modern equivalent of the older git reset HEAD <file>.
Difficulty:Intermediate
You made some experimental changes to a file but want to discard them entirely and revert to the version from your last commit.
git restore <filename>
git restore <file> copies the version of the file from the index (staging area) onto the working tree, discarding any unstaged edits. If the file has never been staged since the last commit, the index already matches HEAD, so this also matches the last commit. If you have a partially-staged file and want to go all the way back to HEAD (blowing away both staged and unstaged edits), use git restore --source=HEAD --staged --worktree <file>.
Difficulty:Advanced
You merge a feature branch into main, and Git performs the merge without creating a new merge commit — it simply moves the ‘main’ pointer forward. What type of merge is this, and when does it occur?
A fast-forward merge — occurs when ‘main’ has no new commits since the feature branch was created.
A fast-forward merge happens when the target branch hasn’t diverged. Git can simply advance its pointer in a straight line to the tip of the incoming branch, keeping the commit history perfectly linear. If both branches have diverged, Git performs a three-way merge and creates a merge commit instead.
Difficulty:Advanced
You want to safely inspect the codebase at a specific older commit without modifying any branch. How do you do this?
git switch --detach <commit-hash>
git switch --detach places you in ‘detached HEAD’ state, where HEAD points directly to a specific commit rather than a branch. You can look around freely, but any commits made here are not anchored to a branch and can be lost when switching away. Use git switch -c <name> to anchor them to a new branch if needed.
Difficulty:Intermediate
You want to safely ‘undo’ a previous commit that introduced an error, but you don’t want to rewrite history or force-push. How do you create a new commit with the exact inverse changes?
git revert
git revert is the safest way to undo changes on a shared branch because it adds a new commit that nullifies the targeted commit, rather than deleting history.
Difficulty:Intermediate
You want to see exactly what has changed in your working directory compared to your last saved snapshot (the most recent commit).
git diff HEAD
Using git diff HEAD compares your current modified tracked files on disk with the most recent commit. Note that it does not show completely untracked files.
Difficulty:Basic
You are starting a brand new project in an empty folder on your computer and want Git to start tracking changes in this directory.
git init
Initializes a new, empty Git repository in the current directory, creating the hidden .git folder that holds all version-control data. It is a one-time setup step per project.
Difficulty:Basic
You have just installed Git on a new computer and need to set up your username and email address so that your commits are properly attributed to you.
git config
Using git config --global user.name and user.email establishes your default identity, which can be overridden for specific projects using git config --local.
Difficulty:Basic
You’ve made changes to three different files, but you only want two of them to be included in your next snapshot. How do you move those specific files to the staging area?
git add <filename>
The git add command moves file modifications from the working directory into the staging area (index) to prepare them for the next commit.
Difficulty:Basic
You’ve lost track of what you’ve been doing. You want a quick overview of which files are modified, which are staged, and which are completely untracked by Git.
git status
Summarizes the working tree and staging area — what is modified, staged, or untracked — without changing anything. It is the cheapest way to orient yourself before staging or committing.
Difficulty:Basic
You have staged all the files for a completed feature and are ready to permanently save this snapshot to your local repository’s history with a descriptive message.
git commit -m 'message'
Records everything currently in the staging area as a new, immutable snapshot in your local history. Only staged changes are included — unstaged edits stay in the working directory.
Difficulty:Basic
You want to review the chronological history of all past commits on your current branch, including their author, date, and commit message.
git log
This command git log prints out the commit history. You can also add flags like -p to see the exact patch/diff introduced in each commit.
Difficulty:Intermediate
You’ve made edits to a file but haven’t staged it yet. You want to see the exact lines of code you added or removed compared to what is currently in the staging area.
git diff
Running git diff without any arguments compares your current working directory against the staging area.
Difficulty:Basic
You want to create a new branch pointer for a future feature without switching branches yet. Which command creates that branch at your current commit?
git branch <branch-name>
This command git branch creates a new branch pointer at your current commit, allowing you to diverge from the main line of development.
Difficulty:Basic
You are currently on your feature branch and need to switch your working directory back to the ‘main’ branch.
git switch main
The modern git switch command navigates between branches, updating your working directory and moving HEAD. The legacy equivalent git checkout main still works and is widely seen in older documentation and scripts.
Difficulty:Intermediate
Your feature branch is complete, and you want to integrate its entire commit history into your current ‘main’ branch.
git merge <branch-name>
Merging with git merge takes the divergent histories of two branches and combines them, often resulting in a new ‘merge commit’ with two parents.
Difficulty:Basic
You want to start working on an open-source project hosted on GitHub. How do you download a full local copy of that repository to your machine?
git clone <url>
Cloning with git clone downloads the entire repository, including its full history and all branches, from a remote server to your local disk.
Difficulty:Intermediate
Your team members have uploaded new commits to the shared remote repository. You want to fetch those changes and immediately integrate them into your current local branch.
git pull
The git pull command is effectively a combination of git fetch and git merge (the default) or git rebase (if configured), integrating remote changes into your local branch.
Difficulty:Basic
You have finished making several commits locally and want to upload them to the remote GitHub repository so your team can see them.
git push
git push transmits your local commits to the remote server, updating the remote branch to match your local branch.
Difficulty:Intermediate
You have a specific commit hash and want to see detailed information about it, including the commit message, author, and the exact code diff it introduced.
git show <commit-hash>
git show displays the details of a specific Git object, most commonly used to inspect exactly what changed in a single commit.
Difficulty:Intermediate
You want to start working on a new feature in isolation. How do you create a new branch called ‘feature-auth’ and immediately switch to it in a single command?
git switch -c feature-auth
git switch -c creates a new branch and switches to it in one step. The -c flag stands for ‘create’. The legacy equivalent is git checkout -b feature-auth, which you will still encounter in older scripts and documentation.
Difficulty:Intermediate
You accidentally staged a file you didn’t intend to include in your next commit. How do you move it back to the working directory without losing your modifications?
git restore --staged <filename>
git restore --staged unstages a file by copying the version from the last commit back into the staging area, leaving your working directory modifications completely untouched. It is the modern equivalent of the older git reset HEAD <file>.
Difficulty:Intermediate
You made some experimental changes to a file but want to discard them entirely and revert to the version from your last commit.
git restore <filename>
git restore <file> copies the version of the file from the index (staging area) onto the working tree, discarding any unstaged edits. If the file has never been staged since the last commit, the index already matches HEAD, so this also matches the last commit. If you have a partially-staged file and want to go all the way back to HEAD (blowing away both staged and unstaged edits), use git restore --source=HEAD --staged --worktree <file>.
Difficulty:Advanced
You merge a feature branch into main, and Git performs the merge without creating a new merge commit — it simply moves the ‘main’ pointer forward. What type of merge is this, and when does it occur?
A fast-forward merge — occurs when ‘main’ has no new commits since the feature branch was created.
A fast-forward merge happens when the target branch hasn’t diverged. Git can simply advance its pointer in a straight line to the tip of the incoming branch, keeping the commit history perfectly linear. If both branches have diverged, Git performs a three-way merge and creates a merge commit instead.
Difficulty:Intermediate
You have some uncommitted, incomplete changes in your working directory, but you need to switch to another branch to urgently fix a bug. How do you temporarily save your current work without making a messy commit?
git stash
git stash temporarily shelves your staged and unstaged changes for tracked files. To include brand new, untracked files in the stash, you must use git stash -u.
Difficulty:Intermediate
You know a bug was introduced recently, but you aren’t sure which commit caused it. How do you perform a binary search through your commit history to find the exact commit that broke the code?
git bisect
git bisect systematically halves your commit history, asking you to test if the bug is present at each step, making it highly efficient for tracking down regressions.
Difficulty:Intermediate
You are looking at a file and want to know exactly who last modified a specific line of code, and in which commit they did it.
git blame
git blame annotates each line of a file with the author and the commit hash of the last modification, which is very useful for figuring out who to ask about a piece of code.
Difficulty:Advanced
You have a feature branch with several experimental commits, but you only want to move one specific, completed commit over to your main branch.
git cherry-pick
git cherry-pick allows you to selectively grab a specific commit from one branch and apply it onto your current branch, without merging the entire branch history.
Difficulty:Advanced
You want to integrate a feature branch into main, but instead of bringing over all 15 tiny incremental commits, you want them combined into one clean commit on the main branch.
git merge --squash
Squashing collapses every patch from the feature branch into one set of changes, so a single clean commit lands on the target branch. It only stages those changes, though — you must follow it with git commit to materialise the squashed commit.
Difficulty:Advanced
You are building a massive project and want to include an entirely separate external Git repository as a subdirectory within your project, while keeping its history independent.
git submodule add <url> <path>
git submodule add clones the external repo into <path> and records a gitlink (mode 160000) pointing at its current commit SHA, plus a .gitmodules entry with the URL. The outer repo stores just that pinned SHA — no file duplication. Teammates get the content via git clone --recursive (or git submodule update --init after a plain clone).
Difficulty:Advanced
Instead of creating a merge commit, you want to take the commits from your feature branch and re-apply them directly on top of the latest ‘main’ branch to create a clean, linear history.
git rebase main
Rebasing with git rebase rewrites history by moving the base of your current branch to the tip of another, which creates a cleaner history but should be avoided on public/shared branches.
Difficulty:Advanced
You want to safely inspect the codebase at a specific older commit without modifying any branch. How do you do this?
git switch --detach <commit-hash>
git switch --detach places you in ‘detached HEAD’ state, where HEAD points directly to a specific commit rather than a branch. You can look around freely, but any commits made here are not anchored to a branch and can be lost when switching away. Use git switch -c <name> to anchor them to a new branch if needed.
Difficulty:Intermediate
Stringa=newString("hello");Stringb=newString("hello");System.out.println(a==b);// Line ASystem.out.println(a.equals(b));// Line B
Predict each output. Then explain why Line A and Line B differ — what does each operator actually check?
Line A:false. Line B:true.
== checks reference identity — are a and b the same object in memory? new String(...) forces a fresh heap allocation each time, so a and b are two different objects.
.equals() checks value equality — do the two Strings contain the same characters? They do, so it returns true.
Key insight: Java’s == is always a reference comparison for objects. Unlike Python’s == (value comparison) and C++’s == (overloadable per class), Java’s == never examines content.
The only change is 127 → 128. What mechanism in the JVM causes this flip, and why is this dangerous in production code?
Java pre-creates and caches Integer objects for values −128 to 127 at JVM startup. Autoboxing Integer x = 127 hands back the same cached object every time, so x == y is true (same reference).
Outside the cache range, Integer.valueOf(128) creates a new heap object each call. p and q point to different objects — == returns false.
Why dangerous: Tests usually use small values (IDs, counts) that fall in the cache range. == appears to work. In production, large IDs or counts fall outside the cache and == silently returns false. The fix is always .equals() for wrapper types.
Difficulty:Intermediate
Integercount=null;intn=count;// what happens here?
Describe exactly what the JVM does on the second line and what error results.
Auto-unboxing is syntactic sugar. The second line expands to:
intn=count.intValue();
Calling .intValue() on null throws a NullPointerException — the JVM cannot dereference a null reference.
This is a common production bug because Integer fields default to null (not 0), and database queries returning no row often produce null wrapper values.
Difficulty:Advanced
// Version AIntegersum=0;for(inti=0;i<1_000_000;i++){sum+=i;}// Version Bintsum=0;for(inti=0;i<1_000_000;i++){sum+=i;}
Both produce the same final value. Analyze what the JVM does differently in Version A on every iteration. Which version should you use?
Version A expands sum += i to:
sum=Integer.valueOf(sum.intValue()+i);
That is two method calls and one new Integer object allocation per iteration — one million short-lived objects in total, generating garbage-collector pressure.
Version B performs pure stack arithmetic — no objects created, no method calls.
Use Version B. Use primitive types for accumulators and counters. Use wrapper types only when generics (List<Integer>), nullable values, or object methods (.compareTo()) require them.
The fields are public. Explain what specific harm this causes compared to making them private with a withdraw() method that validates before mutating.
With public fields, any class can set balance to any value — including negative amounts or values that bypass business rules. There is no enforcement point.
account.balance=-999.0;// completely legal — no way to prevent this
With private double balance and a withdraw() method, all mutations go through one gate where you can enforce invariants:
The field is private. A colleague says “information hiding is achieved.” Are they right? What would break if you later switch scores to int[]?
Wrong. The field is encapsulated (private), but information hiding is not achieved.
The return type ArrayList<Integer>exposes the storage decision as part of the public API. Every caller of getScores() now depends on ArrayList. If you switch to int[]:
// These callers break immediately:ArrayList<Integer>s=report.getScores();s.iterator();// int[] has no iterator()
Proper information hiding exposes behavior, not data structure:
getAverage() — hides that you store an ArrayList
getLetterGrade(int score) — hides the grading policy
formatReport() — hides the output format
After this refactoring, switching from ArrayList to int[] changes exactly one class.
Explain what @Override buys you here. Give an example of the specific bug it prevents.
@Override instructs the compiler to verify that the annotated method actually overrides something in the parent class or interface.
Without it, a typo silently creates a new method instead of overriding:
// Without @Override — compiles, but never called polymorphicallypublicdoublegetArae(){returnMath.PI*radius*radius;}// typo!
With @Override:
@OverridepublicdoublegetArae(){...}// COMPILE ERROR: method does not override
It also catches the case where an interface method is renamed — the override becomes a dead method silently if @Override is absent.
Difficulty:Advanced
abstractclassVehicle{privateStringmake;publicVehicle(Stringmake){this.make=make;}publicStringgetMake(){returnmake;}publicabstractStringdescribe();}classCarextendsVehicle{publicCar(Stringmake){super(make);// ← this line}@OverridepublicStringdescribe(){returngetMake()+" Car";}}
Why must super(make) be the first statement in Car’s constructor? What would happen if it were moved after getMake()?
Java requires the parent’s constructor to run before any subclass code, because the subclass may depend on state initialized by the parent. If super() is not the first statement, a partially-constructed Vehicle could be accessed.
If you tried to move super(make) below a getMake() call:
publicCar(Stringmake){getMake();// compile error — super() must come firstsuper(make);}
The compiler enforces this: if no explicit super() or this() is the first line, Java implicitly inserts super() (the no-arg parent constructor). If the parent has no no-arg constructor, compilation fails.
In C++, the equivalent constraint is enforced through initializer lists: Car(String make) : Vehicle(make) { }.
The reference type is Vehicle, but describe() is abstract. Describe precisely what happens at compile time and at runtime when v.describe() is called.
Compile time: The compiler checks that describe() is declared in the static type of v, which is Vehicle. Since Vehicle declares abstract String describe(), the call is legal. The compiler does not know which implementation will run.
Runtime: The JVM uses dynamic dispatch (virtual method table lookup). It examines the actual type of the object — Car or Motorcycle — and calls that class’s describe() implementation. The reference type Vehicle is irrelevant at this point.
This is Java’s default behavior for all non-static, non-final methods. Unlike C++, where virtual dispatch requires the virtual keyword, every Java method is effectively virtual.
Why does swap declare its own type parameters <X, Y> instead of reusing the class’s <A, B>?
swap is static — it has no access to the instance’s type parametersA and B, because statics exist on the class, not on any particular Pair<A, B> instance.
<X, Y> are fresh parameters scoped to this method call, letting the compiler infer types from the argument:
If swap used A and B directly, a static method would need to belong to a specific Pair<A, B>, which is impossible. The method-level parameters <X, Y> make swap work for anyPair, not just one with a specific concrete type.
Difficulty:Advanced
Map<String,Integer>scores=newHashMap<>();scores.put("Alice",95);intgrade=scores.get("Bob");// Bob not in map
This compiles without warnings. Predict what happens at runtime and explain the chain of events.
Runtime:NullPointerException.
Step-by-step:
scores.get("Bob") — "Bob" is not a key, so HashMap.get() returns null (not 0, not an exception)
int grade = null — auto-unboxing expands this to null.intValue()
Calling .intValue() on a null reference throws NullPointerException
This is one of the most common Java production bugs: HashMap silently returns null, and the NPE appears at the unboxing site, which is often far from where the missing key was introduced.
Difficulty:Intermediate
publicclassSafeCalculator{publicdoubledivide(inta,intb)throwsCalculatorException{if(b==0)thrownewCalculatorException("Division by zero");return(double)a/b;}}classCalculatorExceptionextendsException{publicCalculatorException(Stringmsg){super(msg);}}
CalculatorException extends Exception, not RuntimeException. What concrete difference does this choice produce for callers of divide()?
Because CalculatorException is checked (extends Exception), the compiler forces every caller to either:
Or propagate it: public void run() throws CalculatorException { ... }
If CalculatorException extended RuntimeException, callers could ignore it entirely — the exception would propagate silently until it crashed the program at runtime.
The design choice says: “Division by zero is a recoverable situation the caller should explicitly decide how to handle” — not a programmer error. This is appropriate for library APIs where you want to force callers to think about failure modes.
Difficulty:Intermediate
// Version Apublicdoubleaverage(ArrayList<Integer>scores){...}// Version Bpublicdoubleaverage(List<Integer>scores){...}
Both compile. Analyze the practical difference when other code calls average().
Version A forces callers to pass an ArrayList specifically:
average(newArrayList<>(...));// worksaverage(newLinkedList<>(...));// worksaverage(Arrays.asList(1,2,3));// works — Arrays.asList returns a List, not ArrayList
Version B is correct. The parameter should be the narrowest interface that expresses what the method actually needs. average() only needs to iterate — that’s a List contract, not an ArrayList one. Using ArrayList couples callers to an implementation detail the method doesn’t actually require.
Predict each output and explain what design principle drives the difference between HashSet and ArrayList.
submitted.size() → 1
roster.size() → 2
HashSet implements the Set contract: a collection with no duplicate elements. add() silently ignores values already present.
ArrayList implements the List contract: an ordered sequence that allows duplicates. Each add() appends unconditionally.
Design principle: The collection type encodes your invariant — what the data is allowed to contain. Choosing HashSet for submitted assignments is not just a performance choice; it’s a semantic declaration that “a student can only submit once.”ArrayList gives you no such guarantee.
The tradeoff: HashMap loses insertion order (use LinkedHashMap to preserve it), but gains O(1) lookup. For a Course.isEnrolled() called frequently (e.g., every time someone tries to enroll), the O(1) version is significantly better.
Information-hiding perspective: Because students is private, this switch only modifies one class — no callers break. This is the payoff of information hiding.
Difficulty:Basic
Two String variables input and stored may or may not point to the same object. Write a boolean expression that checks whether they contain the same characters, guaranteed to be correct regardless of how they were created.
input.equals(stored)
.equals() compares character content regardless of object identity. == would fail whenever input and stored are separate objects with identical content — for example, when stored came from a database query and input came from user input.
Difficulty:Intermediate
A HashMap lookup is crashing in production with a NullPointerException. The code is:
Fix it in one line, defaulting to 0 for missing students.
int g = grades.getOrDefault(studentId, 0);
HashMap.get() returns null for missing keys. Auto-unboxing null to int throws NPE. getOrDefault(key, fallback) returns the value if present, or the fallback safely — no null, no NPE. Alternatively: grades.containsKey(studentId) ? grades.get(studentId) : 0, but getOrDefault is more idiomatic.
Difficulty:Advanced
Design a BankAccount class that:
Stores owner (String) and balance (double) — neither directly accessible from outside
Provides a constructor, getOwner(), getBalance()
deposit(double amount) — only accepts positive amounts
withdraw(double amount) — returns false if insufficient funds; true on success
Private fields + validated methods = encapsulation. The withdraw boolean return avoids throwing an exception for an expected condition (insufficient funds isn’t a programming error). toString() is annotated @Override — the compiler verifies we’re overriding Object.toString().
Difficulty:Expert
This class has a design problem. Identify it, then rewrite GradeReport so that changing the grading thresholds (A ≥ 90, B ≥ 80…) requires editing only one method:
classGradeReport{privateList<Integer>scores;publicList<Integer>getScores(){returnscores;}}// In main:for(ints:report.getScores()){if(s>=90)System.out.println("A");elseif(s>=80)System.out.println("B");}
Problem: The grading policy leaks into main. Changing thresholds requires editing every call site.
classGradeReport{privateList<Integer>scores=newArrayList<>();publicvoidaddScore(intscore){scores.add(score);}// Hides the grading policy — change thresholds in ONE placepublicStringgetLetterGrade(intscore){if(score>=90)return"A";if(score>=80)return"B";if(score>=70)return"C";if(score>=60)return"D";return"F";}// Hides the data representation — callers never see ArrayListpublicdoublegetAverage(){intsum=0;for(ints:scores)sum+=s;returnsum/(double)scores.size();}// Hides the output format — change to CSV here, nothing else changespublicStringformatReport(Stringname){StringBuildersb=newStringBuilder("Report: "+name+"\n");for(ints:scores)sb.append(" ").append(s).append(" (").append(getLetterGrade(s)).append(")\n");sb.append("Average: ").append(getAverage());returnsb.toString();}}
This is Parnas’s information hiding principle in action. Three secrets are now hidden: grading policy (in getLetterGrade), data representation (no getScores() exposed), output format (in formatReport). Changing any of them touches exactly one method.
Difficulty:Intermediate
Define a Drawable interface with one method: String draw(). Then write a Square class that implements it — draw() returns "Square(side=5.0)".
Interface methods are implicitly public abstract. The implementing class uses implements (not extends). Every method declared in the interface must be implemented — the compiler enforces this. @Override confirms the method matches the interface signature.
Difficulty:Advanced
Design an abstract class Animal with:
A private String name and a constructor
A concrete getName() getter
An abstract method makeSound() that returns a String
Then write a Dog subclass that calls the parent constructor and returns "Woof!" from makeSound().
publicabstractclassAnimal{privateStringname;publicAnimal(Stringname){this.name=name;}publicStringgetName(){returnname;}publicabstractStringmakeSound();}publicclassDogextendsAnimal{publicDog(Stringname){super(name);// must be first — initializes parent's private field}@OverridepublicStringmakeSound(){return"Woof!";}}
super(name) must be the first statement — it runs the parent constructor before any Dog-specific code. Because name is private in Animal, Dog cannot access it directly — it uses getName(). The abstract keyword on makeSound() forces every concrete subclass to provide an implementation.
Difficulty:Intermediate
Write a generic class Box<T> that holds one item of any type. Include a constructor, a getItem() method, and a setItem() method.
publicclassBox<T>{privateTitem;publicBox(Titem){this.item=item;}publicTgetItem(){returnitem;}publicvoidsetItem(Titem){this.item=item;}}// Usage:Box<String>nameBox=newBox<>("Alice");Stringname=nameBox.getItem();// no cast needed — compiler knows it's StringBox<Integer>numBox=newBox<>(42);intn=numBox.getItem();// auto-unboxing Integer → int
<T> is a type parameter — a placeholder filled in at the call site. The compiler uses it to insert correct types and casts, catching mismatches at compile time instead of runtime. Box<int> is illegal — use Box<Integer> since generics require object types (due to type erasure).
Difficulty:Advanced
Write a generic static method findMax that takes two arguments of any type and returns the larger one. The type must be constrained to types that can be compared.
<T extends Comparable<T>> is a bounded type parameter — it restricts T to types that implement Comparable. This is the Java equivalent of C++20 concepts. Without the bound, the compiler would reject a.compareTo(b) because it can’t guarantee T has that method. String, Integer, Double all implement Comparable.
Difficulty:Expert
Write a WordCounter class that takes a String[] in its constructor and provides:
int getCount(String word) — returns 0 for unknown words, no NPE
int getUniqueCount() — number of distinct words
Use the most appropriate collection for each responsibility.
importjava.util.HashMap;importjava.util.HashSet;publicclassWordCounter{privateHashMap<String,Integer>counts=newHashMap<>();privateHashSet<String>unique=newHashSet<>();publicWordCounter(String[]words){for(Stringw:words){unique.add(w);// HashSet deduplicatescounts.put(w,counts.getOrDefault(w,0)+1);// HashMap counts}}publicintgetCount(Stringword){returncounts.getOrDefault(word,0);// safe — no NPE on missing key}publicintgetUniqueCount(){returnunique.size();}}
HashMap<String, Integer> maps each word to its frequency — O(1) insert and lookup. HashSet<String> tracks distinct words — O(1) add with automatic deduplication. getOrDefault(word, 0) avoids the NPE that get() followed by auto-unboxing would cause for missing keys.
Difficulty:Advanced
Define a checked exception EnrollmentException and a Course.enroll(Student s) method that throws it when the course is full (capacity exceeded). Write both the class definition and the calling code that handles the exception.
publicclassEnrollmentExceptionextendsException{publicEnrollmentException(Stringmessage){super(message);// passes message to Exception's constructor}}publicclassCourse{privateintcapacity;privateList<Student>students=newArrayList<>();publicCourse(intcapacity){this.capacity=capacity;}publicvoidenroll(Students)throwsEnrollmentException{if(students.size()>=capacity){thrownewEnrollmentException("Course is full");}students.add(s);}}// Calling code — compiler forces handlingtry{course.enroll(newStudent("Alice",1001));}catch(EnrollmentExceptione){System.out.println("Could not enroll: "+e.getMessage());}
Extending Exception (not RuntimeException) makes it checked — callers must catch or re-throw it. The throws EnrollmentException in the method signature is part of the contract and required by the compiler. super(message) stores the message in Exception, making it available via getMessage().
Difficulty:Intermediate
Write a try-catch-finally block that: opens a file (throws IOException), reads its content, and prints an error if it fails. The finally block should always print "Done.".
catch (IOException e) handles IOException and all its subclasses (e.g., FileNotFoundException). finally runs whether or not an exception was thrown — use it for cleanup (closing streams, releasing locks). Even if the catch block re-throws, finally still runs.
Difficulty:Advanced
You need to store course enrollments. Two options:
List<Student> with a manual duplicate check in enroll()
LinkedHashSet<Student> that handles duplicates automatically
Implement enroll(Student s) using each approach, then state which is preferable and why.
// Option A: List — manual duplicate checkprivateList<Student>students=newArrayList<>();publicvoidenroll(Students){if(!students.contains(s))students.add(s);}// Option B: LinkedHashSet — automatic deduplication, insertion order preservedprivateLinkedHashSet<Student>students=newLinkedHashSet<>();publicvoidenroll(Students){students.add(s);// ignored if already present — no check needed}
Option B is preferable.LinkedHashSet.add() is O(1) and the “no duplicates” invariant is enforced by the type — you cannot accidentally skip the check. List.contains() is O(n) and the invariant is only enforced if every enroll() caller remembers to check. Choose a collection whose contract matches your invariant.
Choosing the right collection encodes intent. Set means ‘unique elements’ — the type enforces the invariant. List means ‘ordered sequence with possible duplicates’ — you must enforce uniqueness manually, which is fragile. LinkedHashSet adds insertion-order iteration over plain HashSet.
Difficulty:Intermediate
Write a method printAll(List<String> items) that iterates the list with an enhanced for-loop, printing each item. Then call it with an ArrayList<String>and a LinkedList<String>. Explain why both calls compile.
publicvoidprintAll(List<String>items){for(Stringitem:items){System.out.println(item);}}// Both compile because both implement List<String>List<String>array=newArrayList<>(Arrays.asList("a","b"));List<String>linked=newLinkedList<>(Arrays.asList("c","d"));printAll(array);// fineprintAll(linked);// fine
Declaring the parameter as List<String> (the interface) rather than ArrayList<String> (the concrete class) allows any List implementation to be passed. This is ‘program to an interface, not an implementation.’ The enhanced for-loop works on any Iterable, which both ArrayList and LinkedList implement through List.
Difficulty:Expert
You are building a course registration system. Design the method signature (interface method + throws) for an Enrollable interface that:
Adds a student (can fail if course is full or duplicate)
Removes a student by name (returns whether it succeeded)
Checks enrollment by name
Returns a list of enrolled student names
importjava.util.List;publicinterfaceEnrollable{// Throws checked exception — caller must handle enrollment failuresvoidenroll(Studentstudent)throwsEnrollmentException;// Returns false if student not found — boolean, not exception (expected condition)booleandrop(Stringname);// Pure lookup — no side effects, no exceptionbooleanisEnrolled(Stringname);// Returns names only — hides Student objects and concrete storage from callerList<String>getRoster();}
enroll() throws a checked exception because a full course is an external condition the caller should handle. drop() returns boolean because dropping a non-existent student is a non-exceptional expected case. getRoster() returns List<String> (names) rather than exposing Student objects or a concrete collection type — this hides both the internal Student type and the storage decision from consumers of the interface.
Difficulty:Advanced
A teammate wrote this accumulator. Find the performance issue, explain the root cause, and write the corrected version.
Issue:total++ expands to total = Integer.valueOf(total.intValue() + 1) — an object allocation on every iteration for an Integer that is immediately discarded.
Root cause:Integer is a wrapper (heap object). Using it as an accumulator creates one new object per loop iteration, generating garbage-collector pressure.
// Corrected — use primitive int for the counterinttotal=0;for(Stringword:words){if(word.length()>5)total++;}
Use Integer only when the type system requires it: generic containers (List<Integer>), nullable fields, or calling methods like .compareTo(). For local counters and accumulators, always use int.
Difficulty:Basic
What does void* malloc(size_t size) return on success, and what does it return when the OS cannot satisfy the request?
On success, malloc returns a void* pointing to a freshly-allocated block of at least size bytes on the heap. The block’s contents are uninitialized — they hold whatever bytes were last in that memory.
On failure (the OS cannot give you that many bytes), malloc returns NULL.
You must check for NULL before dereferencing the result. Forgetting this check turns an out-of-memory condition into a silent segfault, often far from the actual allocation site.
Difficulty:Intermediate
In C, what is '\0'? Distinguish it from '0' and explain why C strings need it.
'\0' is the null terminator — the byte with ASCII value 0. It is not the digit '0', which has ASCII value 48.
Every C string is a char array that ends with '\0'. Library functions in <string.h> (strlen, strcpy, strcmp, …) walk the array byte-by-byte until they hit the terminator — that’s how they know where the string ends, because C strings carry no length metadata.
A char array without a terminator is not a string for these functions; they will read past the end of the array.
Difficulty:Advanced
Why does C have no function overloading? Explain the design tradeoff.
C does not mangle function names — every symbol in an object file is the literal name you wrote in the source. Overloading would require encoding parameter types into the symbol name (the C++ approach), which C explicitly avoids.
The tradeoff: C source code has to use distinct names like printInt / printFloat instead of two print overloads, but C symbols are callable from any other language because the symbol you wrote is the symbol the linker sees. This is the main reason almost every language has a clean foreign-function interface to C, but calling into C++ requires extra work to handle mangled names.
Difficulty:Intermediate
Explain the difference between char and char* in C.
charc='A';char*s="Alice";
char c = 'A' is a single byte holding the ASCII value 65. It lives directly in the variable’s storage.
char* s = "Alice" is a pointer holding the address of the first byte of the string literal "Alice\0" in read-only memory. The literal itself is 6 bytes (5 characters plus the null terminator); s holds an 8-byte address (on a 64-bit system).
Modifying c is fine — it’s an ordinary local variable. Writing through s (e.g., s[0] = 'B') is undefined behavior because string literals live in read-only memory.
%zu is the format specifier for size_t (the type sizeof returns); on most platforms sizeof(int) is 4.
Negative-transfer trap from C++: mismatched format specifiers (%d for a float, %f for an int) compile cleanly but produce garbage at runtime, because printf blindly trusts the format string. Always compile with -Wall -Wformat to catch this.
Difficulty:Intermediate
Write a C function void swap(int* a, int* b) that swaps the values pointed to by a and b, plus the call site that swaps two local variables x and y.
voidswap(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}intmain(void){intx=30,y=40;swap(&x,&y);// x is now 40, y is now 30}
Key points:
The function dereferences with *a to read or write the caller’s int.
The call site writes &x, &y — C has no pass-by-reference, so the caller has to take the address explicitly.
The signature swap(int*, int*) advertises that the function may mutate both arguments. That advertisement is more explicit than C++’s swap(int&, int&).
Difficulty:Advanced
Allocate a flat rows × cols matrix of int on the heap, write the index expression for element (i, j) in row-major order, and free the allocation.
int*matrix=malloc(rows*cols*sizeof(int));if(matrix==NULL){fprintf(stderr,"out of memory\n");return1;}// Element (i, j) in row-major order:matrix[i*cols+j]=42;free(matrix);matrix=NULL;// defensive — turns accidental reuse into a fast segfault
sizeof(int) is needed because malloc takes a byte count, not an element count. Forgetting it (malloc(rows * cols)) would allocate only one byte per element, and the index expression would walk into unrelated memory.
Difficulty:Advanced
What is the bug in this code, and what is the most likely runtime symptom?
buf is a stack-allocated local array. When greeting returns, that stack frame is reclaimed; the pointer the caller receives points into memory the runtime may reuse for the next function call.
Symptom: Often the call site prints the right string on the first read (because no other function has reused that stack space yet), then garbage after the next function call clobbers the buffer. This is the classic “returns-pointer-to-stack” bug — and because the first read often works, it can ship for months before manifesting under load.
Fix options:
Allocate on the heap: char* buf = malloc(64); ...; return buf; — caller must free.
Have the caller pass a buffer: void greeting(char* buf, size_t n);.
Return a pointer to a static buffer — works but is not thread-safe.
Difficulty:Intermediate
What is the role of libc, and how does it relate to operating-system system calls?
libc is the C standard library. It provides a portable API (fopen, malloc, printf, strlen, …) that your C program calls.
Inside libc, those functions ultimately invoke system calls — privileged operations the kernel performs on behalf of user-space programs. The system-call ABI is different on every operating system (the open syscall on Linux takes different arguments than the equivalent on Windows).
libc papers over those differences. The result: your fopen call compiles into different machine code on Linux vs. macOS vs. Windows, but your source is identical. libc is the layer that makes C source portable across operating systems.
Difficulty:Advanced
Walk through what happens at runtime when this code executes:
int*p=malloc(sizeof(int));*p=7;free(p);free(p);
Line 1 allocates 4 bytes on the heap; p holds its address.
Line 2 writes 7 into that block.
Line 3 (free(p)) returns the block to the allocator. The allocator marks it free; the pointer variable p still holds the old address.
Line 4 is a double-free — undefined behavior. Concretely:
If the allocator has not yet reused that block, it sees a freed entry being freed again and corrupts its internal bookkeeping. The next malloc may return the same address to two callers, or may crash inside the allocator.
If the block has already been re-allocated to another caller, this free returns their memory to the allocator. They will eventually write through a freed pointer.
Defensive habit:free(p); p = NULL; makes a subsequent free(p) a no-op (the standard guarantees free(NULL) does nothing).
Difficulty:Expert
Name two distinct production scenarios where you would deliberately choose C over C++, and explain why each scenario favors C.
1. Aerospace / medical / safety-critical firmware. NASA’s coding rules restrict flight software to a subset that excludes exceptions and most polymorphism, because hidden control-flow edges (every try block, every virtual call) make it harder for automated verification tools to prove the program meets its specification. C’s smaller, more predictable control-flow graph buys you analyzability.
2. Cross-language library. If you write a high-performance routine that callers in Python, Java, Rust, and Go all need to use, exposing a C ABI is the lingua franca. C symbols are unmangled, so every language’s foreign-function interface can bind to them directly. A C++ library forces every caller to deal with mangled names, ABI versions, and runtime initialization.
Either reason can justify the discipline cost of manual memory management.
Difficulty:Advanced
Almost every major language (Python, Java, C#, Rust, Go, Ruby) supports calling into a C library. Browser JavaScript does not — and this is not an accident. What is the design rationale?
Browser JavaScript runs inside a security sandbox that prevents pages from touching your filesystem, hardware, or arbitrary memory. C has unrestricted access to all of those.
If browser JavaScript could call native C functions on your machine, a malicious page could read your private files, drive attached devices, or execute arbitrary code — the entire sandbox guarantee would evaporate.
The modern workaround is WebAssembly: you compile C source to Wasm bytecode, which the browser runs in the same sandbox as JavaScript. Wasm has no syscall access by default — it can only see what the page explicitly hands it. You keep the performance of C without breaking the browser’s security model.
Difficulty:Advanced
Design a C struct for a singly-linked-list node that stores an int value. Then write the prototype for a function list_prepend that takes the current head and an int, and returns the new head.
structlist_node{intvalue;structlist_node*next;// self-referential pointer; NULL marks end};// Returns a new head whose `next` is the old head; caller owns the returned pointer.structlist_node*list_prepend(structlist_node*head,intvalue);
Design choices worth naming:
The self-reference must be a pointer: struct list_node* next has a fixed pointer size, while embedding a full struct list_node next would require an infinitely large object.
The function returns a new head rather than mutating in place — this lets the caller chain prepends and handle an empty list (head == NULL) uniformly.
The doc comment names ownership: the caller must eventually walk the list and free each node.
Difficulty:Advanced
Compare static and dynamic linking on three axes: when linking happens, what gets shipped, and the consequence for security updates.
Axis
Static linking
Dynamic linking
When
At build time
At program-start time (or lazily, on first call)
Ships
One self-contained executable with library code copied in
Executable + separate .so / .dll files the OS resolves at load time
Security update
Every program statically linked against the buggy library must be re-linked and re-shipped
One library file replaced; every dynamically-linked program picks up the fix on next start
The tradeoff in one sentence: static linking maximizes portability and run-time predictability at the cost of binary size and update agility; dynamic linking does the reverse. Embedded firmware tends toward static; OS distributions tend toward dynamic.
Difficulty:Basic
What is the standard syntax to define a basic build rule in a Makefile?
target: prerequisites
command
A rule defines the target file to be built, the prerequisite files it depends on, and the command(s) to execute to build it.
Difficulty:Basic
What specific whitespace character MUST be used to indent the command/recipe lines in a Makefile rule?
A Tab character
If you use spaces instead of a Tab, make will throw an error (often ‘missing separator’).
Difficulty:Basic
How do you reference a variable (or macro) named ‘CC’ in a Makefile command?
$(CC) or ${CC}
Variables are defined using an equals sign (e.g., CC = gcc) and referenced by wrapping the name in parentheses or curly braces preceded by a dollar sign.
Difficulty:Basic
What Automatic Variable represents the file name of the target of the rule?
$@
This is commonly used in the compilation command to specify the output file name dynamically.
Difficulty:Intermediate
What Automatic Variable represents the name of the first prerequisite?
$<
This is frequently used in pattern rules to pass the specific source file (like a .c file) to the compiler.
Difficulty:Intermediate
What Automatic Variable represents the names of all the prerequisites, with spaces between them?
$^
This is often used in linking commands where all object files need to be passed to the compiler at once to create an executable.
Difficulty:Intermediate
What wildcard character is used to define a Pattern Rule (a generic rule applied to multiple files)?
% (Percent sign)
For example, %.o: %.c tells make how to compile any .o object file from a corresponding .c source file.
Difficulty:Intermediate
What special target is used to declare that a target name is an action (like ‘clean’) and not an actual file to be created?
.PHONY: target_name
This prevents conflicts if a file literally named ‘clean’ is ever created in the directory, ensuring the recipe will always run when invoked.
Difficulty:Advanced
What metacharacter can be placed at the very beginning of a recipe command to suppress make from echoing the command to the terminal?
@ (At symbol)
For example, @echo "Done!" will print ‘Done!’ to the terminal without printing the actual ‘echo’ command first.
Difficulty:Advanced
What syntax is used for string substitution on a variable, such as changing all .c extensions in $(SRCS) to .o?
$(SRCS:.c=.o)
This evaluates to the value of the SRCS variable, but replaces the suffix .c with .o for every word in the list.
Difficulty:Intermediate
Write a basic Makefile rule to compile a single C source file (main.c) into an executable named app.
app: main.c
gcc main.c -o app
This defines app as the target, main.c as the prerequisite, and provides the exact compilation command required to build the executable. (Remember the command must be indented with a Tab).
Difficulty:Intermediate
Write a Makefile snippet that defines variables for the C compiler (gcc) and standard compilation flags (-Wall -g), and uses them to compile main.c into main.o.
Variables make the Makefile flexible. If you later want to switch to clang or add optimization flags, you only need to change the variable definitions at the top.
Difficulty:Advanced
Write a standard clean target that removes all .o files and an app executable, ensuring it runs even if a file literally named ‘clean’ is created in the directory.
.PHONY: clean
clean:
rm -f *.o app
The .PHONY declaration tells make that clean is an action, not a file. The -f flag in rm prevents errors if the files don’t exist.
Difficulty:Advanced
Write a generic pattern rule to compile any .c file into a corresponding .o file, using automatic variables for the target name and the first prerequisite.
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
The % acts as a wildcard. $< substitutes the name of the .c file, and $@ substitutes the name of the .o file.
Difficulty:Advanced
Given a variable SRCS = main.c utils.c, write a variable definition for OBJS that dynamically replaces the .c extension with .o for all files in SRCS.
OBJS = $(SRCS:.c=.o)
This is a substitution reference. It takes the value of SRCS, looks for the .c suffix at the end of each word, and replaces it with .o.
Difficulty:Advanced
Write a rule to link an executable myprog from a list of object files stored in the $(OBJS) variable, using the automatic variable that lists all prerequisites.
myprog: $(OBJS)
$(CC) $^ -o $@
$^ expands to a space-separated list of all prerequisites (all the .o files), and $@ expands to the target name (myprog).
Difficulty:Intermediate
Write the conventional default target rule that is used to build multiple executables (e.g., app1 and app2) when a user simply types make without specifying a target.
all: app1 app2
By convention, all is the first target in a Makefile. Since make defaults to the first target it sees, this guarantees both apps are built. No commands are needed; simply listing them as prerequisites triggers their respective build rules.
Difficulty:Advanced
Write a run target that executes an output file named ./app, but suppresses make from printing the command to the terminal before running it.
run: app
@./app
Adding the @ symbol at the very beginning of the recipe line tells make to execute the command silently (without echoing it to standard output first).
Difficulty:Advanced
Write a variable definition SRCS that uses a Make function to dynamically find and list all .c files in the current directory.
SRCS = $(wildcard *.c)
The wildcard function tells make to search the filesystem and return a space-separated list of all files matching the given pattern.
Difficulty:Intermediate
Write a generic rule to create a build directory build/ using the mkdir command.
build/:
mkdir -p $@
Directories can be targets just like regular files. The -p flag ensures mkdir doesn’t throw an error if the directory already exists.
Difficulty:Intermediate
Given the snippet app: main.o network.o utils.o followed by the command $(CC) $(CFLAGS) $^ -o $@, what exactly does the command evaluate to if CC=gcc and CFLAGS=-Wall?
gcc -Wall main.o network.o utils.o -o app
$^ expands to all prerequisites (the three .o files), and $@ expands to the target name (app). This is the standard way to link object files into a final C executable.
Difficulty:Intermediate
If a C project Makefile contains SRCS = main.c math.c io.c and OBJS = $(SRCS:.c=.o), what does OBJS evaluate to?
main.o math.o io.o
This is a substitution reference. It iterates through every file listed in SRCS, finds the .c extension, and replaces it with .o to dynamically generate the list of object files needed for compilation.
Difficulty:Intermediate
Read this common pattern rule: %.o: %.c followed by $(CC) $(CFLAGS) -c $< -o $@. If make uses this rule to build utils.o from utils.c, what does $< represent?
utils.c
In a pattern rule, $< is an automatic variable that evaluates to the first prerequisite. In this case, it passes the specific C source file being compiled to the compiler.
Difficulty:Advanced
You see the line CC ?= gcc at the top of a Makefile. What happens if a developer compiles the project by typing make CC=clang in their terminal?
The compiler used will be clang, not gcc.
The ?= operator is a conditional assignment. It only sets CC to gcc if CC has not already been defined. The command-line argument overrides the default.
Difficulty:Intermediate
A C project has a rule clean:
rm -f *.o myapp. Why is it critical to also include .PHONY: clean in this Makefile?
To ensure the cleanup runs even if a developer accidentally creates a file literally named clean in the project directory.
Without .PHONY, if a file named clean exists and has no prerequisites, make will think the target clean is already “up to date” and will refuse to execute the rm command.
Difficulty:Advanced
In the rule main.o: main.c main.h types.h, what happens if you edit and save types.h?
make will recompile main.o the next time it is run.
Even though types.h is not directly passed to the compiler in the recipe, listing header files as prerequisites tells make to track their modification timestamps. If a header changes, the object files that depend on it must be rebuilt.
Difficulty:Advanced
You are reading a Makefile and see @echo "Compiling $@..." followed by @$(CC) -c $< -o $@. What do the @ symbols do?
They suppress the terminal from printing the actual commands before executing them.
Instead of seeing the messy gcc command printed out, the developer will only see the clean, custom message: Compiling main.o....
Difficulty:Intermediate
What is the conventional purpose of the CFLAGS variable in a C Makefile?
To store flags and options passed to the C compiler (e.g., warnings and optimizations).
Common values include -Wall (enable all warnings), -Wextra (extra warnings), -g (include debugging symbols), or -O2 (optimize for speed).
Difficulty:Advanced
What is the conventional purpose of the LDFLAGS or LDLIBS variables in a C Makefile?
To store flags and library links passed to the linker during the final executable creation.
For example, if your C program uses the math library <math.h>, you would define LDLIBS = -lm so the linker knows to include it when combining the .o files.
Difficulty:Intermediate
A C project has multiple executables: a server and a client. The Makefile starts with all: server client. What happens if you just type make?
It will build both the server and the client executables.
make defaults to the first target defined in the file (which is all here). By listing both programs as prerequisites for all, it forces make to evaluate and build the rules for both of them.
Workout Complete!
Your Score: 0/304
Come back later to improve your recall!
Tools Master Quiz
A comprehensive mix of the standalone tools quizzes: shell, regular expressions, programming-language essentials, Git, Java, C, and Make.
Difficulty:Intermediate
A developer needs to parse a massive log file, extract IP addresses, sort them, and count unique occurrences. Instead of writing a 500-line Python script, they use grep | cut | sort | uniq -c. Why is this approach fundamentally preferred in the UNIX environment?
The UNIX benefit here is composability, not a guarantee that shell pipelines are faster than
compiled programs. The pipeline works because each tool accepts and emits a simple stream.
cut and sort are ordinary programs using OS services. The design win is the shared
text-stream interface, not bypassing the kernel.
Pipelines can avoid temporary files and stream data between processes, but they do not prevent
the OS from allocating memory. The core idea is composition through standard streams.
Correct Answer:
Explanation
This pipeline follows the UNIX philosophy of combining single-purpose tools that communicate via text streams. Each program ‘does one thing well’ and they cooperate through a shared text-stream interface, so complex tasks emerge from chaining simple tools rather than writing one large program.
Difficulty:Intermediate
A script runs a command that generates both useful output and a flood of permission error messages. The user runs script.sh > output.txt, but the errors still clutter the terminal screen while the useful data goes to the file. What underlying concept explains this behavior?
The terminal is showing stderr because only stdout was redirected. There is no security rule
that mirrors redirected output back to the screen.
The shebang controls which interpreter runs the script, not which stream > redirects. This
behavior comes from stdout and stderr being separate file descriptors.
Stderr can be redirected explicitly, for example with 2> errors.txt. It stays on the terminal
here only because the command redirected stdout alone.
Correct Answer:
Explanation
Errors still appear in the terminal because stdout and stderr are separate streams, and > only redirects stdout. A process has three default streams (stdin, stdout, stderr); > captures only stdout, so the errors need their own redirection such as 2> errors.txt.
Difficulty:Advanced
A C++ developer writes a Bash script with a for loop. Inside the loop, they declare a variable temp_val. After the loop finishes, they try to print temp_val expecting it to be undefined or empty, but it prints the last value assigned in the loop. Why did this happen?
Assignment in Bash creates a shell variable, not an exported environment variable. export is
needed before child processes inherit it.
let performs arithmetic evaluation; it does not declare a C++-style block-local variable. Bash
loop bodies do not create a new lexical scope.
Privilege does not change Bash scoping rules. sudo affects permissions and identity, not
whether loop variables remain visible after the loop.
Correct Answer:
Explanation
Bash lacks block-level scoping, so variables set inside loops remain accessible throughout the entire script. Unlike C++ {} blocks, loops and conditionals do not create a new scope. Unless a variable is declared local inside a function, its value persists after the control structure ends.
Difficulty:Advanced
You want to use a command that requires two file inputs (like diff), but your data is currently coming from the live outputs of two different commands. Instead of creating temporary files on the disk, you use the <(command) syntax. What is this concept called and what does it achieve?
A pipe gives one command a single stdin stream. diff needs two file-like inputs, so process
substitution is the better fit.
Redirection changes stdin, stdout, or stderr for a process. Process substitution creates a
filename-like handle that a command can open as an input file.
<(command) does not evaluate arithmetic or return exit codes. It exposes the command output
through a temporary file descriptor.
Correct Answer:
Explanation
This is process substitution — it lets the OS present a command’s output as a temporary file descriptor without writing to disk. The OS exposes the command’s stdout through a file-like handle (such as /dev/fd/63) that the consuming command opens as if it were a real file — which is why it works with tools like diff that expect file arguments.
Difficulty:Intermediate
A script contains entirely valid Python code, but the file is named script.sh and has #!/bin/bash at the very top. When executed via ./script.sh, the terminal throws dozens of ‘command not found’ and syntax errors. What is the fundamental misunderstanding here?
UNIX does not execute a file based on the .sh extension. The executable bit and the shebang
determine how ./script.sh is launched.
Python scripts can be executable when they have execute permission and a Python shebang. They do
not have to be invoked with the python command every time.
set -e changes failure handling; it cannot make Bash understand Python syntax. The interpreter
selection is wrong before that option would help.
Correct Answer:
Explanation
The shebang #! dictates the interpreter, completely overriding the file extension — which the OS treats as superficial. The first line’s shebang tells the program loader which interpreter (Bash, Python, Node, …) parses the rest of the file. A .sh name with #!/bin/bash runs everything through Bash, so valid Python is read as broken shell syntax.
Difficulty:Intermediate
A developer uses the regular expression [0-9]{4} to validate that a user’s input is exactly a four-digit PIN. However, the system incorrectly accepts ‘12345’ and ‘A1234’. What crucial RegEx concept did the developer omit?
Digits in [0-9] are already being matched intentionally. The missing piece is requiring the
match to cover the whole input.
* repeats the previous atom; it is not a general instruction to validate the entire string.
Anchors are what bind a pattern to the input boundaries.
Named groups help retrieve parts of a match after it succeeds. They do not prevent extra
characters from appearing before or after the match.
Correct Answer:
Explanation
Anchors were omitted, so the regex matches the valid substring inside the input and ignores the surrounding characters.[0-9]{4} matches anywhere, so ‘12345’ succeeds because it contains ‘1234’. Wrapping the pattern in ^...$ forces it to span the entire input, rejecting any extra characters.
Difficulty:Advanced
You are designing a data pipeline in the shell. Which of the following statements correctly describe how UNIX handles data streams and command chaining? (Select all that apply)
One process writes stdout, and the next process reads that same data as stdin. That stream
connection is the core pipe model.
>> matters because it appends stdout instead of replacing the file. That is a different
operation from ordinary > redirection.
A pipe carries stdout by default, not stderr. Error text needs explicit redirection if it should
join the pipeline.
< file is the input-side counterpart to output redirection. It lets a command read stdin from
a file rather than from the keyboard or previous pipe.
>! is not the Bash mechanism for redirecting both stdout and stderr. In Bash, common forms are
&> or explicit descriptor redirection such as >out 2>err.
Correct Answers:
Explanation
| links stdout to stdin, >> appends, and < redirects stdin — but pipes do NOT carry stderr and >! is not standard Bash. A plain pipe leaves stderr on the terminal, so error text needs explicit redirection to join the stream. To send both streams to the same place Bash uses &> or descriptor forms like >out 2>err, not >!.
Difficulty:Intermediate
You’ve written a shell script deploy.sh but it throws a ‘Permission denied’ error or fails to run when you type ./deploy.sh. Which of the following are valid reasons or necessary steps to successfully execute a script as a standalone program? (Select all that apply)
./deploy.sh asks the OS to execute that file, so execute permission must be present. Read
permission alone is not enough for direct execution.
Shell scripts are interpreted text, not C/C++ source. Compiling with gcc is unrelated to
running a Bash script.
The shebang is how the OS knows which interpreter should read the script. Without it, direct
execution may use the wrong shell or fail.
./deploy.sh already gives a path to the file. $PATH is only searched when the command name
has no slash.
Correct Answers:
Explanation
A script needs execute permissions (chmod +x) and a valid shebang — it is interpreted, not compiled, and ./ bypasses $PATH. The execute bit lets the OS run the file; the shebang says which interpreter reads it. No compilation is involved, and the leading ./ already names the path, so the directory need not be on $PATH.
Difficulty:Advanced
In Bash, exit codes are crucial for determining if a command succeeded or failed. Which of the following statements are true regarding how Bash handles exit statuses and control flow? (Select all that apply)
false is useful precisely because it does no work except return a failing status. It is a tiny
command for testing shell control flow.
set -e changes the script into fail-fast mode for many unhandled nonzero statuses, but it is
not a blanket “any nonzero exits” rule. Bash ignores -e in several control-flow contexts.
Shell success is exit status 0; nonzero values indicate failure. That is the reverse of
treating 1 as boolean true.
Bash if runs a command and checks its exit status. It is not looking for a boolean object
named true or false.
Correct Answers:
Explanation
Exit code 0 means success; if tests exit codes not booleans; and set -e creates fail-fast behavior for many unhandled failures. In UNIX, an exit code of 0 means success, while non-zero (like 1) means failure/error. if statements work by running a command and proceeding if its exit code is 0. set -e is useful, but Bash intentionally does not exit for every nonzero status: tests in if/while, non-final commands in &&/|| lists, non-final pipeline commands unless pipefail is set, and commands inverted with ! are common exceptions.
Difficulty:Advanced
When you type a command like python or grep into the terminal, the shell knows exactly what program to run without you providing the full file path. How does the $PATH environment variable facilitate this, and how is it managed? (Select all that apply)
$PATH is an explicit ordered list of directories. The shell searches those directories in
order until it finds an executable name.
Changing $PATH in a terminal affects that shell session and child processes. A new terminal
will not inherit it unless startup files recreate the change.
Startup files such as ~/.bashrc or ~/.zshrc are how a user makes the path change happen
again in future shells.
The shell does not crawl the whole disk for commands. That would be slow, unpredictable, and
unsafe compared with an explicit path list.
Correct Answers:
Explanation
$PATH is an explicit ordered directory list, not a global crawler — session changes are temporary unless saved to a startup file. The shell checks the listed directories in order and stops at the first match (crawling the whole disk would be slow and unsafe). An export PATH=... in the terminal lasts only for that session unless it is added to a startup file like ~/.bashrc.
Difficulty:Intermediate
A developer writes LOGFILE="access errors.log" and then runs wc -l $LOGFILE. The command fails with ‘No such file or directory’ errors for both ‘access’ and ‘errors.log’. What is the root cause?
wc can process files whose names contain spaces. The shell split the unquoted variable before
wc received the arguments.
Variable case did not cause this failure. The problem is unquoted expansion followed by word
splitting on the embedded space.
wc -l does not care that the file ends in .log. The command failed because one intended
filename became two arguments.
Correct Answer:
Explanation
The shell word-splits the unquoted variable at the space, turning one filename into two separate arguments.wc -l $LOGFILE expands to wc -l access errors.log, so wc looks for two files. Quoting the expansion — wc -l "$LOGFILE" — keeps the value as a single argument.
Difficulty:Basic
A script is invoked with ./deploy.sh production 8080 myapp. Inside the script, which variable holds the value 8080?
$0 is the script path or command name. User-supplied positional arguments start at $1.
$1 is production in this invocation. 8080 is the second user-supplied argument, so it is
$2.
$# is the argument count, which would be 3 here. It does not hold the value of any
particular argument.
Correct Answer:
Explanation
$2 holds the value 8080 because $0 is the script name, $1 is the first argument, and $2 is the second.$0 is the script name (./deploy.sh), $1 is the first argument (production), $2 is the second argument (8080), and $3 would be myapp. $# holds the total argument count (3).
Difficulty:Intermediate
A script contains the line: cd /deploy/target && ./run_tests.sh && echo 'All tests passed!'. If ./run_tests.sh exits with a non-zero status code, what happens next?
&& skips later commands after a failure, but it does not by itself mean the whole script must
exit in every context. It is a short-circuit operator.
The last command in an && chain only runs if every earlier command succeeded. A failing test
command prevents the success message.
Shell control operators do not retry commands automatically. Retrying requires an explicit loop
or retry command in the script.
Correct Answer:
Explanation
&& short-circuits when ./run_tests.sh fails, so echo 'All tests passed!' is never executed. A non-zero exit code stops the chain — every remaining &&-linked command is skipped. The success message runs only when every preceding command exits 0.
Difficulty:Advanced
Which of the following statements correctly describe Bash quoting and command substitution behavior? (Select all that apply)
Single quotes are the strongest ordinary quoting form: the content is taken literally, including
$ and $(...).
Double quotes preserve spaces while still allowing variable and command substitution. That
combination is why they are common around variable expansions.
Quoting "$FILE" keeps a filename with spaces as one argument. Without the quotes, the shell
performs word splitting.
Single and double quotes are not interchangeable when expansion is involved. Single quotes
suppress $...; double quotes allow it.
$(command) runs the command and substitutes its stdout into the surrounding command line.
Stderr is not captured unless redirected.
Correct Answers:
Explanation
Single quotes suppress all expansion, double quotes allow substitution while protecting spaces, and $(command) captures output — single and double quotes are not interchangeable. Because single quotes block $... and double quotes do not, the two are only interchangeable when no expansion is needed. Quoting "$VARIABLE" is what stops word splitting on values that contain spaces.
Difficulty:Advanced
Arrange the pipeline fragments to build a command that extracts all ERROR lines from a log, sorts them, removes duplicates, and counts how many unique errors remain.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The pipeline is grep → sort → uniq → wc -l; cat is unnecessary and > would write to a file instead of piping.grep filters for ERROR lines, sort orders them (required before uniq), uniq removes adjacent duplicates, and wc -l counts the remaining lines. Each | pipes stdout to the next command’s stdin — the UNIX philosophy of chaining single-purpose tools. cat is unnecessary here (UUOC — Useless Use of Cat) and > would redirect to a file rather than pipe.
Difficulty:Expert
Arrange the lines to write a shell script that validates a command-line argument, prints an error to stderr if missing, and exits with a non-zero code. Otherwise it prints a logging message.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: #!/bin/bash if [ $# -lt 1 ]; then echo "Error: no filename given" >&2 exit 1 fi echo "Processing $1..."
Explanation
The script starts with the shebang, uses [ $# -lt 1 ] to check arguments, redirects errors to stderr with >&2, exits with 1 on failure, and closes the conditional with fi. The shebang (#!/bin/bash) must be the first line. The if [ $# -lt 1 ] test checks argument count, >&2 redirects echo to stderr, and exit 1 terminates with a failure code. The distractor return 1 only works inside functions, not at the top level of a script. Bash closes an if block with fi, not endif.
Difficulty:Expert
Arrange the pipeline fragments to find the 5 most frequently occurring IP addresses in an access log.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The pipeline is grep -oE → sort → uniq -c → sort -rn → head -5; tail -5 gives the least frequent IPs, not the most. The grep -oE extracts all IPv4 addresses (one per line). sort groups identical IPs together (required for uniq). uniq -c counts each group. sort -rn sorts by count in descending numeric order. head -5 takes the top 5. tail -5 would give the least frequent, and wc -l would just count total lines.
Difficulty:Advanced
Arrange the fragments to redirect both stdout and stderr of a deployment script into a single log file.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: ./deploy.sh>output.log2>&1
Explanation
> output.log then 2>&1 captures both streams to the file — order matters because reversing them sends stderr to the terminal.> output.log redirects stdout to the file first, then 2>&1 redirects stderr to wherever stdout currently points (the file). Order matters: 2>&1 > output.log would send stderr to the original stdout (terminal) and only stdout to the file. The distractor 1>&2 goes the wrong direction — it sends stdout to stderr. Using 2> output.log alone still lets stdout reach the terminal.
Difficulty:Advanced
Arrange the pipeline to count how many files under src/ contain the word TODO.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: grep -rl 'TODO' src/|wc -l
Explanation
grep -rl prints one filename per match (not per occurrence), so wc -l correctly counts matching files — grep -r without -l would count lines, not files.grep -rl (recursive, files-with-matches) prints one matching filename per line — each file counted once regardless of how many matches it contains. wc -l then counts those filenames. The distractor grep -r (without -l) prints every matching line, so wc -l would count occurrences, not files. sort is unnecessary here since we only need a count.
Difficulty:Intermediate
Arrange the fragments to grant execute permission on a script and immediately run it.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: chmod +x script.sh&&./script.sh
Explanation
chmod +x makes the file executable, && ensures it only runs on success, and ./script.sh uses the shebang — || would run it only on failure.chmod +x script.sh adds the execute bit, making the file runnable as a program. && ensures the script only runs if chmod succeeded. ./script.sh executes it using the shebang to select the interpreter. Using || instead of && would run the script only when chmodfails — the opposite of the intent. sh script.sh works but bypasses the shebang, hardcoding the interpreter to sh regardless of what the script specifies.
Difficulty:Intermediate
You are working inside project/ which currently has this structure:
Detailed description
Folder tree rooted at project/ with 2 folders and 3 files. Top-level entries: README.md, src/.
Entries
project/ (folder)
README.md (file)
src/ (folder)
app.js (file)
utils.js (file)
You run mkdir src/components/ui. What is the result?
Plain mkdir does not create missing parents. Use mkdir -p src/components/ui when
intermediate directories may be absent.
Without -p, mkdir fails before creating the missing intermediate directory. It does not
create only the first missing parent.
A filesystem path cannot skip the intermediate components directory. The parent path must
exist before ui can be created inside it.
Correct Answer:
Explanation
Without -p, mkdir requires every parent to already exist — src/components/ is missing, so the command errors and nothing is created. Plain mkdir creates only the final path segment. Adding -p (mkdir -p src/components/ui) creates all missing parents in one idempotent operation.
Difficulty:Intermediate
You are working inside project/ which currently has this structure:
Detailed description
Folder tree rooted at project/ with 4 folders and 5 files. Top-level entries: README.md, build/, src/.
Entries
project/ (folder)
README.md (file)
build/ (folder)
main.o (file)
helper.o (file)
output/ (folder)
app (file)
src/ (folder)
app.c (file)
You run rm build/ from inside project/. What is the result?
Plain rm refuses to remove directories. Recursive deletion requires -r, which is
intentionally explicit because it can delete many files.
rm build/ does not partially empty the directory. Without -r, it fails at the directory
argument.
rm is not a trash command. When deletion is allowed, it removes directly rather than moving
files to /tmp.
Correct Answer:
Explanation
rm without -r refuses to remove directories — it only deletes regular files. Plain rm build/ prints rm: cannot remove 'build/': Is a directory and changes nothing. Recursive deletion needs rm -r build/; adding -f (rm -rf build/) also suppresses prompts and missing-file errors — the silent, forceful variant used in scripts.
Difficulty:Advanced
Arrange the fragments to find which lines appear most often in access.log — showing the top 5 repeated entries with their counts.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
sort groups identical lines together so uniq can process them. uniq -c prefixes each unique line with its occurrence count. The second sort -rn reorders by that count, highest first. head -5 takes the top 5. Using cat before sort is redundant — sort reads the file directly. tail -5 would show the least repeated lines instead.
Difficulty:Advanced
Arrange the fragments to count how many unique lines containing "error" (case-insensitive) exist in app.log.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
grep -i filters lines matching error in any case. sort groups identical lines together — uniq only removes adjacent duplicates, so sorting first is required. uniq collapses the duplicates. wc -l counts the remaining unique lines. Dropping -i would miss ERROR or Error. Using head instead of wc -l would print lines rather than count them.
Difficulty:Intermediate
Arrange the fragments to combine two log files and display every unique line in sorted order.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: cat server.log error.log|sort|uniq
Explanation
cat server.log error.log concatenates both files into a single stream. sort groups identical lines adjacent to each other — required because uniq only collapses adjacent duplicates. uniq then removes the duplicates. Using grep with a filename instead of cat would filter rather than merge. wc -l would count lines instead of printing them.
Difficulty:Advanced
Arrange the fragments to display only the non-comment, non-blank lines from config.txt, sorted alphabetically.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
grep -v '^#' removes lines that start with # (comments). grep -v '^$' removes blank lines (lines matching the empty-string anchor). sort alphabetically orders the remaining entries. The distractor grep '^#' would keep only comment lines — the opposite of the goal. wc -l would count rather than display the lines.
Difficulty:Intermediate
Arrange the fragments to count how many .txt files are in the current directory.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: ls|grep '\.txt$'|wc -l
Explanation
ls lists all entries in the current directory. grep '\.txt$' filters for names ending in .txt (the $ anchors to end-of-line; the \. matches a literal dot). wc -l counts the matching lines. Using grep '\.py$' would count Python files instead. sort would order the names but not count them.
Difficulty:Advanced
You are tasked with extracting all data enclosed in HTML <div> tags. You write a regular expression, but it consistently fails on deeply nested divs (e.g., <div><div>text</div></div>). From a theoretical computer science perspective, why is standard RegEx the wrong tool for this?
The problem is not scan direction. Arbitrarily nested tags require memory for matching open and
close levels, which regular expressions do not provide.
< and > can be matched literally. Escaping characters does not turn a regular expression
into a parser for nested structure.
Catastrophic backtracking is a performance problem in some regexes, but the deeper issue here is
language shape: nested HTML is not regular.
Correct Answer:
Explanation
RegEx fails on nested structures because it is backed by Finite State Machines, which lack the stack memory needed to track balanced nesting. Regular expressions correspond to regular languages; arbitrarily nested HTML is context-free, which needs a stack-based parser to count opening and closing pairs.
Difficulty:Advanced
A developer writes a regex to parse a log file: ^.*error.*$. They notice that while it works, it runs much slower than expected on very long log lines. What underlying behavior of the .* token is causing this inefficiency?
The engine is not slow because it starts at the end. The expensive behavior is greedy
consumption followed by backtracking.
.* is greedy by default. A lazy version would be written .*?, and it changes how much the
quantifier initially consumes.
The engine does not need to cache the entire file because of .*. The waste is repeated trial
and backtracking within the candidate line.
Correct Answer:
Explanation
.* is greedy — it consumes the whole string first, then backtracks character by character, which is expensive on long lines. Greedy quantifiers grab as much as possible immediately, so .* swallows the line, discovers it overshot ‘error’, and steps backward one character at a time until the rest of the pattern fits.
Difficulty:Advanced
You need to validate user input to ensure a password contains both a number and a special character, but you don’t know what order they will appear in. What mechanism allows a RegEx engine to assert these conditions without actually ‘consuming’ the string character by character?
A non-capturing group organizes syntax, but it still consumes the characters it matches. It does
not assert an independent condition at the same position.
Possessive quantifiers control backtracking after a match attempt. They do not express unordered
requirements such as “has a digit and has a symbol.”
Word boundaries assert positions around word characters. They do not test for multiple required
character categories.
Correct Answer:
Explanation
Lookaheads are the right tool because they assert conditions without consuming characters, enabling multiple requirements to be chained at the same position. Being zero-width, several lookaheads can sit at the start of a pattern and each check the whole string independently — acting like a logical AND across unordered requirements.
Difficulty:Advanced
You are given the regex (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}) and apply it to the string 2026-04-01. After a successful match, which of the following correctly describes how you can access the captured month value?
Named groups still receive numeric positions. The name adds a readable access path; it does not
remove positional access.
The group can be accessed by name, but not only by name. It is still the second capturing group
in the pattern.
The captured month is stored in the match object. Inspecting the original regex string cannot
recover what the input matched.
Correct Answer:
Explanation
Named groups can be accessed by both name and positional index because they are a strict superset of numbered groups. Naming a group adds a readable label (match.group('month')) but does not remove its position — it is still capturing group 2, reachable as match.group(2).
Difficulty:Intermediate
When writing a complex regex to extract phone numbers, you use parentheses (...) to group the area code so you can apply a ? quantifier. However, you also want to extract the area code by name for later use in your code. What is the best approach?
Lookaheads are for checking conditions without consuming text. They are not the right mechanism
when the goal is to capture and later read the area code.
Removing parentheses would make it unclear what the ? applies to. Parentheses are how a
multi-token area code becomes one optional unit.
Escaped parentheses match literal ( and ) characters. They no longer group or capture
pattern text.
Correct Answer:
Explanation
A named group (?<areaCode>...) is best because it both enables the quantifier and provides a readable name for accessing the captured value. Standard parentheses (...) create groups that can be accessed by index (e.g., match[1]). Named groups (?<name>...) let you assign a meaningful label, so you can access the matched value by name (e.g., match.groups.areaCode) — making your code self-documenting and easier to maintain.
Difficulty:Intermediate
You write a regex to ensure a username is strictly alphanumeric: [a-zA-Z0-9]+. However, a user successfully submits the username admin!@#. Why did this happen?
A character class is exactly the right syntax for “one alphanumeric character.” The bug is that
the whole input was not anchored.
+ repeats only alphanumeric characters from the class. The punctuation is accepted because the
unanchored regex can stop after matching admin.
Case-insensitive matching affects letter case, not punctuation. Symbols are ignored here because
they sit outside the unanchored substring match.
Correct Answer:
Explanation
Without ^ and $ anchors, the regex matches any valid substring and ignores the rest of the input. The pattern found admin, counted that as a match, and never looked at the trailing !@#. Anchoring both ends forces the pattern to account for the entire string — mandatory for strict validation.
Difficulty:Advanced
Which of the following scenarios are highly appropriate use cases for Regular Expressions? (Select all that apply)
IPv4-like text is a bounded, mostly flat pattern, so regex can describe useful candidates well.
Deep JSON has nested structure and escaping rules that need a parser. Regex may find snippets,
but it should not be trusted to parse the payload.
A strict date shape such as YYYY-MM-DD is a good regex-sized constraint, especially before
deeper date validation.
HTML sanitization is security-sensitive and context-dependent. A parser plus context-aware
escaping is safer than trying to strip tags with regex.
Capture groups are well suited for rearranging flat text formats, such as swapping date fields
in a document.
Correct Answers:
Explanation
RegEx is appropriate for flat pattern matching and format validation, but not for tree-structured data like JSON or HTML. RegEx excels at pattern matching flat strings, validating strict formats, and doing structural find-and-replace operations. It should NOT be used to parse complex, tree-like structures like JSON or HTML, as it cannot handle infinite nesting and is prone to security bypasses.
Difficulty:Intermediate
In the context of evaluating a regex for data extraction, what represents a ‘False Positive’ and a ‘False Negative’? (Select all that apply)
A false positive is a match that should have been rejected. It usually means the pattern is too
permissive.
Rejecting invalid text is a true negative, not a false negative. The pattern did the right thing
in that case.
A false negative is valid target text that the regex failed to match. It usually means the
pattern is too strict or misses a valid variant.
A syntax error means the pattern did not execute. False positives and false negatives describe
outcomes of a running matcher.
Correct Answers:
Explanation
A False Positive is an unwanted match (regex too loose); a False Negative is a missed match (regex too strict). Tightening a pattern to kill false positives tends to introduce false negatives, and loosening it does the reverse — balancing the two is the core tension when writing patterns.
Difficulty:Intermediate
You use the regex <.*> to extract a single HTML tag from <b>bold</b> text, but it matches the entire string <b>bold</b> instead of just <b>. What is the simplest fix?
.+ still uses a greedy quantifier, so it can still consume through the last >. Requiring one
character does not make the match shorter.
Parentheses group or capture; they do not change greediness. The * would still try to consume
as much as possible.
The global flag finds multiple matches, but each individual match can still be too large. The
quantifier needs to be lazy or more specific.
Correct Answer:
Explanation
Adding ? makes the quantifier lazy (*?) so it stops at the first > rather than greedily consuming everything. A greedy * runs from the first < to the last>; the lazy form matches as few characters as possible, ending the match at the first > it reaches.
Difficulty:Advanced
Which of the following statements about Lookaheads (?=...) are true? (Select all that apply)
A lookahead checks what follows while leaving the main match position unchanged. That is why it
can enforce conditions without adding to the result.
Multiple lookaheads can be placed at the same position to test independent requirements over the
same input.
Lookaheads are not part of basic POSIX regular expressions. Support depends on the regex engine,
so standard grep and sed cannot be assumed to handle them.
Chained lookaheads are a common way to express “must contain A and B” when the order is not
fixed.
Correct Answers:
Explanation
Lookaheads are zero-width, chainable, and enable logical AND — but they require a PCRE-style engine and are not in basic POSIX tools. Because they add nothing to the match, several can stack at one position to enforce independent ‘must contain’ requirements. Standard grep/sed (without -P) cannot be assumed to support them.
Difficulty:Advanced
Arrange the regex fragments to build a pattern that validates a simple email address like user@example.com. The pattern should be anchored to match the entire string.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The email pattern requires anchors, a local-part character class, a literal @, a domain character class, and a TLD pattern — digits and whitespace are distractors. The pattern anchors with ^ and $ to match the full string. [a-zA-Z0-9._%+-]+ matches the local part (username), @ is the literal at-sign, [a-zA-Z0-9.-]+ matches the domain name, and \.[a-zA-Z]{2,} matches the dot followed by a TLD of at least 2 letters. The distractors \d{3} (three digits) and \s+ (whitespace) have no place in an email pattern.
Difficulty:Intermediate
Arrange the regex fragments to build a pattern that matches a date in YYYY-MM-DD format (e.g., 2024-01-15). Anchor the pattern.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: ^\d{4}-\d{2}-\d{2}$
Explanation
The YYYY-MM-DD pattern uses \d{4}, -, \d{2}, -, \d{2} with anchors; / belongs to a different format and \w+ is too broad. The date pattern uses \d{4} for the 4-digit year, literal - as separators, and \d{2} for 2-digit month and day. Anchors ^ and $ ensure the entire string is a date. The / distractor would be for a different date format (MM/DD/YYYY), and \w+ is too broad (matches letters, digits, and underscores).
Difficulty:Advanced
Arrange the regex fragments to extract the protocol and domain from a URL like https://www.example.com/path. Use a capturing group for the domain.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: https?://([^/]+)
Explanation
https?:// handles both protocols and ([^/]+) captures the domain by matching everything up to the first slash.https?:// matches both http:// and https:// (the s is optional via ?). The capturing group ([^/]+) matches one or more characters that are NOT a forward slash — i.e., the domain name. \w+:// is a distractor that would also match things like ftp:// which wasn’t the intent, and [a-z] matches only a single lowercase letter.
Difficulty:Intermediate
Python is dynamically typed AND strongly typed. JavaScript is dynamically typed AND weakly typed. What is the practical difference for a developer?
Strong and weak typing show up in real bug behavior: Python refuses "5" + 3, while JavaScript
coerces and keeps running.
Strong typing is about whether incompatible operations are coerced, not whether the interpreter
knows all types early enough to optimize them.
Both Python and JavaScript names can be rebound to values of different types; the difference
here is coercion between incompatible values.
Correct Answer:
Explanation
Dynamic typing means types are checked at runtime, not compile time. Strong typing means no implicit coercion between incompatible types: Python raises TypeError on "5" + 3, while JavaScript’s weak typing silently coerces to "53". That refusal is a feature — it prevents a class of silent bugs that plague weakly-typed code.
Difficulty:Basic
In C++, 'A' is a char and "Alice" is a const char* — they are fundamentally different types. A C++ student writes name = 'Alice' in Python and worries they’ve created a character array instead of a string. Are they right?
Python quote style does not choose between character arrays and strings; both quote styles
create str objects.
Python 3 byte strings require a b prefix such as b'Alice'; ordinary single and double quotes
both create Unicode str values.
Python strings are immutable regardless of whether single or double quotes were used.
Correct Answer:
Explanation
Python has no char type — one of the cleanest simplifications over C++. 'Alice' and "Alice" produce identical str objects, so the choice is purely about avoiding backslash escaping: double quotes when the string contains an apostrophe ("It's easy"), single quotes when it contains double quotes ('<div class="box">'). PEP 8 accepts either style but recommends consistency.
Difficulty:Basic
A C++ programmer writes total = sum(scores) / len(scores) and expects integer division (like C++’s /). They get 85.5 instead of 85. What happened, and how should they get integer division?
The float comes from the / operator in Python 3, not from sum() changing integer lists into
floats.
len() returns an integer count; division semantics decide whether the final average is a float
or an integer.
Python did not round the result up; / produced a floating-point quotient, while // is the
operator for integer floor division.
Correct Answer:
Explanation
Python 3’s / ALWAYS returns a float, unlike C++ where int / int truncates to an int. Use // for floor division, which returns an int when both operands are int. This was an intentional Python 3 change to prevent bugs where integer division silently truncated results.
Difficulty:Intermediate
A student writes a function that opens a file, but forgets to close it. Their C++ instinct says ‘this will leak the file handle.’ Is this concern valid in Python, and what is the recommended solution?
Python may eventually close the file, but correctness for files, locks, and sockets needs
deterministic cleanup at block exit.
Python variables going out of scope is not the portable cleanup guarantee; context managers
provide the RAII-like boundary.
with is the standard Python abstraction for try/finally cleanup, and it keeps the resource
protocol local and readable.
Correct Answer:
Explanation
Forgetting f.close() can leave file handles open: the GC will eventually close the file, but ‘eventually’ may be too late — especially in long-running servers or when writing. The with statement is Python’s RAII equivalent: with open('file') as f: guarantees f.close() runs when the block exits, even on exception. Always use with for files, database connections, and locks.
Difficulty:Intermediate
A student uses re.findall(r'ERROR', text) to count errors in a log. Their teammate suggests text.count('ERROR') instead. When is re.findall() the better choice?
Regex carries pattern-matching overhead and complexity; for a fixed literal, text.count()
communicates the intent better.
Python’s str.count() counts substrings too, so "ERROR" is a perfectly valid literal search
target.
A regex pattern language can mean more than the literal characters typed, so it is not
interchangeable with substring search.
Correct Answer:
Explanation
For a fixed literal like ‘ERROR’, text.count('ERROR') is simpler and faster. re.findall() earns its overhead only when the target is a pattern — ‘any sequence of digits’ (r'\d+'), an IP address, a timestamp in HH:MM:SS format. Use the simplest tool that works.
Difficulty:Intermediate
A script needs to report both results (to stdout) and diagnostics (to stderr). A student puts everything in print(). Why is this problematic in a pipeline like python script.py > results.txt?
print() writes to stdout unless told otherwise, so diagnostics need file=sys.stderr to stay
out of pipeline data.
The bug is not the speed of print(); it is sending diagnostic text down the same channel as
machine-readable output.
Pipelines connect processes through standard streams, so Python programs participate just like
Bash, C, or any other executable.
Correct Answer:
Explanation
UNIX separates stdout (file descriptor 1) and stderr (fd 2) for exactly this reason. > results.txt redirects only stdout, so diagnostics sent to print() contaminate the data file. Send them to file=sys.stderr instead — downstream tools then receive clean machine-readable output, the same reason C++ separates std::cout from std::cerr.
Difficulty:Intermediate
A student writes this list comprehension:
result=[x**2forxinrange(1000000)ifx%2==0]
Their teammate says: “This creates a huge list in memory. Use a generator expression instead.” What would the generator version look like, and why is it better?
list(...) forces eager materialization; replacing brackets with parentheses is what creates a
lazy generator expression.
Generator expressions are built into modern Python; itertools.imap() is a Python 2 era false
trail.
Python does not silently turn list comprehensions into generators; brackets mean a list is
allocated.
Correct Answer:
Explanation
Replacing [...] with (...) creates a generator expression. It produces values one at a time using constant memory, instead of building the 500,000-element list upfront. When you only need to iterate once and don’t need to store the full collection, generators are dramatically more memory-efficient.
Python evaluates default arguments once when the function is defined, so the same list is reused
on later calls.
append() mutates lists correctly; the problem is which list object is being mutated across
calls.
Mutable defaults are legal Python; they are dangerous because their shared lifetime is easy to
miss.
Correct Answer:
Explanation
A default argument is evaluated once when the function is defined, not per call, so a mutable default (list, dict, set) is shared across every call that omits it. The fix is to default to None and create a fresh list inside: def add_item(item, items=None): if items is None: items = []; ...
Difficulty:Intermediate
Arrange the lines to define a function that safely reads a file and returns the word count, using with for resource management.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: def count_words(filename): total = 0 with open(filename) as f: for line in f: total += len(line.split()) return total
Explanation
The function opens the file with with, iterates line by line, and accumulates the word count. return total sits at function level (one indent), not inside the loop. f.close() is a distractor because with handles cleanup automatically; return len(f) is wrong because file objects have no meaningful len().
Difficulty:Intermediate
Arrange the lines to create a list comprehension that filters and transforms data, then prints the result.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: scores = [95, 83, 71, 62, 55] passing = [s for s in scores if s >= 70] print(f'Passing scores: {passing}')
Explanation
List comprehensions use the form [expr for var in iterable if condition]. Python lists have no .filter() method — that distractor is borrowed from JavaScript. The f-string in the print statement automatically converts the list to its string representation.
Difficulty:Intermediate
A C++ developer argues: ‘Single-threaded means Node.js can only handle one request at a time, so it’s useless for servers.’ What is the flaw in this reasoning?
Node has worker resources under the hood, but JavaScript request callbacks are not assigned one
OS thread each.
Garbage-collector speed does not solve the architectural point: non-blocking I/O keeps the event
loop available.
V8 executes JavaScript with native machinery, but Node’s concurrency model is still event-loop
driven rather than one C++ thread per request.
Correct Answer:
Explanation
Most server work is I/O-bound (waiting on databases, files, network), not CPU-bound. While the Chef (call stack) does one thing at a time, the Appliances (OS, via libuv) handle I/O in the background, so the single thread stays free to serve other requests. This makes the model highly efficient for I/O-bound work but unsuitable for CPU-heavy tasks like video encoding.
Difficulty:Intermediate
A developer writes this code and is confused why the output is A, C, B instead of A, B, C:
The important rule is not the exact minimum delay; queued callbacks wait until synchronous code
leaves the call stack.
The order is deterministic here because console.log("A") and console.log("C") run
synchronously before the timer callback.
Arrow syntax does not delay execution; the callback is delayed because setTimeout schedules
it.
Correct Answer:
Explanation
A 0ms delay means ‘as soon as possible’, not ‘immediately’ — the callback waits in the Task Queue while A and C run synchronously. The Event Loop never interrupts the Call Stack, so it only hands the B callback over once synchronous code finishes. This is why blocking the main thread with a long loop prevents all callbacks from firing.
Difficulty:Intermediate
A teammate’s code uses == for all comparisons and it ‘works fine in tests.’ You suggest changing to === in code review. They push back: ‘If it works, why change it?’ What is the strongest argument for ===?
The main reason for === is semantic safety against coercion bugs, not speed.
== still exists; the review concern is that implicit coercion can hide type mismatches.
== and === agree only while operands stay the same type, so tests can miss the future
mismatch.
Correct Answer:
Explanation
The danger isn’t that == fails now — it’s that it hides a fragile assumption. When requirements change (e.g., a user ID becomes a string UUID instead of a number), == silently coerces and produces wrong results with no error. === fails loudly when types diverge, catching the bug before it reaches production.
Difficulty:Intermediate
Compare these two approaches for fetching data from two independent APIs:
Parallelizing dependent operations can fail or waste work when the second result needs data from
the first.
Promise.all is about JavaScript scheduling of independent promises, not specifically about
HTTP/2 support.
Avoiding Promise.all for independent work preserves no semantic benefit and can double
avoidable wait time.
Correct Answer:
Explanation
Sequential await is correct when operations depend on each other (fetch a user, then fetch that user’s posts). For independent operations, Promise.all runs them concurrently — max(A, B) instead of A + B. If each fetch takes 200ms, sequential = 400ms but parallel = 200ms.
Difficulty:Intermediate
A student writes var x = 5 inside a for loop body. After the loop, they access x and are surprised it’s still in scope. A C++ programmer would expect x to be destroyed at the closing brace. What JavaScript concept explains this?
var is scoped to the enclosing function; it becomes global only when declared at top level in
the relevant environment.
let and const are block-scoped; the confusing legacy behavior belongs to var.
The loop is not macro-expanded; the observed lifetime follows JavaScript’s var scoping rule.
Correct Answer:
Explanation
var hoists to the enclosing function scope and ignores block boundaries (if, for, while), so it survives past the loop’s closing brace — one of JavaScript’s most confusing legacy features. let and const use block scope, matching the behavior C++ and Python lead you to expect. Always prefer let or const.
Difficulty:Intermediate
Why is the callback pattern fundamental to ALL of Node.js — not just a stylistic choice?
JavaScript has many ways to define functions; callbacks matter because async APIs need a
function to call later.
V8 garbage collection is not why callbacks are used; the event loop needs a continuation for
completed async work.
Promises and async/await improve syntax, but they still represent work that resumes through
scheduled continuations.
Correct Answer:
Explanation
Every async API follows the pattern ‘start this operation and call this function when done’: fs.readFile(path, callback), setTimeout(callback, ms), fetch(url).then(callback) all accept functions. The single-threaded Event Loop has no other way to notify your code that an operation completed. Even async/await is syntactic sugar over Promises, which are themselves built on this pattern.
They expect “All done!” to print after all items are processed. What is the bug?
Marking the callback async makes each callback return a promise, but .forEach() does not
collect or await those promises.
The bug is not the eventual value returned by processItem; it is that the surrounding loop
ignores the promise lifecycle.
Most array iteration helpers are synchronous; await inside their callbacks does not make the
helper itself wait.
Correct Answer:
Explanation
.forEach() has no mechanism to wait for the Promises its async callbacks return — it fires them all and returns immediately, so "All done!" prints before any item finishes. The await inside each callback works, but .forEach itself ignores it. Fix: for (const item of items) { await processItem(item); } (sequential) or await Promise.all(items.map(item => processItem(item))) (parallel).
Difficulty:Advanced
Arrange the lines to write an async function that reads a file and returns its parsed JSON content, handling errors gracefully.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: async function loadConfig(path) { try { const data = await fs.promises.readFile(path, 'utf-8'); return JSON.parse(data); } catch (err) { console.error('Failed to load config:', err.message); return null; } }
Explanation
async/await with fs.promises.readFile reads without blocking, and the try/catch handles both file-not-found and invalid-JSON errors. The readFileSync distractor blocks the Event Loop — the cardinal sin in Node.js. The finally { return data; } distractor would override the return value from both try and catch, a subtle bug since a return in finally wins.
Difficulty:Intermediate
Arrange the lines to set up a basic Express.js route handler that reads a query parameter and sends a JSON response.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
require('express') imports the framework and express() creates the app; app.get() registers a handler where req.query.name reads the ?name= parameter and res.json() replies with correct JSON headers; app.listen(3000) starts the server. The app.post distractor uses the wrong HTTP method for a read, and res.send(name) would send plain text without JSON formatting.
Difficulty:Intermediate
Arrange the fragments to build a Promise chain that fetches data, parses JSON, and handles errors.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
fetch(url) resolves to a Response, .then(res => res.json()) parses the body (also returning a Promise), .then(data => ...) receives the parsed data, and .catch() handles any error in the chain — network failure, parse error, and so on. The .finally distractor receives no argument, so it can’t process data. res.text without () is a property reference, not a method call.
Difficulty:Intermediate
You are building a TikTok-style feed. Match each task to the best array method:
Task A: Remove videos the user has already seen
Task B: Convert each video object into a <VideoCard> component
Task C: Calculate the total watch time across all videos
.map() keeps the same cardinality and transforms each item; it does not remove seen videos.
.reduce() can implement many things, but using it to select unseen videos hides the simpler
operation: filtering.
.reduce() collapses to one accumulated value, so it is the wrong fit for rendering one
component per video.
Correct Answer:
Explanation
.filter() selects elements matching a condition (unseen videos), .map() transforms each element into something new (video → component), and .reduce() combines all elements into one value (total watch time). Knowing which method fits the task — not just how each works — is the critical skill.
Difficulty:Advanced
A Discord bot fetches a user’s message count from an API. The API returns "42" (a string). The bot checks if (count == 42) to award a badge. What are ALL the problems?
The dangerous part is not merely using the wrong operator; the operator hides an API type
mismatch that should be made explicit.
== makes this example pass by accident, which is exactly why the bug can survive until a less
friendly value appears.
Even if the API should return a number, client code still needs explicit conversion or strict
comparison at the boundary.
Correct Answer:
Explanation
== coerces the string "42" to the number 42, so the check passes by accident while hiding a type mismatch === would catch. The accidental success is exactly what lets the bug survive until a less friendly value (like a threshold of 0) appears. Number(count) === 42 makes the conversion explicit and the comparison strict.
Difficulty:Intermediate
Arrange the lines to process an array of Spotify tracks: filter explicit songs, extract just the titles, and join them into a comma-separated string.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
.filter() drops explicit tracks (the ! keeps non-explicit ones), .map() extracts each title string, and .join() concatenates the array into one comma-separated string. The .reduce() distractor accumulates rather than selects, so it’s the wrong tool for filtering. t.title() calls title as a function, but it’s a property, not a method.
Difficulty:Intermediate
What does calling an async function always return, even if the function body just returns a plain number like return 42?
The async keyword always wraps the function result in a Promise, even when the body has no
await.
async functions return Promises; for await...of is for async iterables, a different
protocol.
They can return values, but callers receive those values through the returned Promise.
Correct Answer:
Explanation
An async function always returns a Promise: async function answer() { return 42; } is equivalent to function answer() { return Promise.resolve(42); }. This is why const result = someAsyncFunction() without await gives you a Promise object, not the value — one of the most common async bugs.
Difficulty:Advanced
A developer needs a delay(ms) utility that returns a Promise resolving after ms milliseconds. Which implementation is correct?
setTimeout returns an identifier for canceling the timer, not a Promise that resolves later.
await ms immediately yields the same number because ms is not a Promise tied to a timer.
Wrapping the timer ID with Promise.resolve resolves immediately with the ID; it does not wait
for the callback.
Correct Answer:
Explanation
new Promise(resolve => ...) creates a Promise that settles when resolve is called; passing resolve directly to setTimeout means ‘call resolve after ms milliseconds’, so the Promise resolves exactly when the timer fires. The other approaches return a timer ID immediately, await a non-Promise (resolving instantly), or never return the Promise at all.
Difficulty:Intermediate
Arrange the lines to filter passing students (grade ≥ 60) and extract just their names.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
.filter() selects the passing students first, then .map() transforms the survivors into names. Reversing them fails because .map(s => s.grade >= 60) produces booleans, leaving no grade property to filter on. .filter(s => s.name) keeps every student with a name — all of them — so nothing is filtered, and .reduce accumulates into a single value, not an array.
Difficulty:Advanced
Arrange the lines of a corrected processAll function. The original bug: "All done!" printed before items finished processing because .forEach() ignores the await inside its callback.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: async function processAll(items) { for (const item of items) { await processItem(item); } console.log("All done!"); }
Explanation
for...of is the idiomatic fix for sequential async iteration — await inside it pauses the loop body before the next item. The items.forEach(async ...) distractor is the original bug: .forEach() doesn’t await async callbacks, so all of them fire immediately and "All done!" prints before any finish. await items makes no sense — items is an array, not a Promise.
Difficulty:Advanced
A student writes this code for a multiplayer game server and wonders why player moves are “laggy”:
app.post('/move',(req,res)=>{// Compute best AI response (CPU-intensive, ~2 seconds)constaiMove=computeAIResponse(req.body.board);res.json({move:aiMove});});
What is wrong, and what would you suggest?
async changes how waiting is expressed, but a synchronous two-second computation still
occupies the event loop thread.
The lag comes before res.json(), while the server is unable to handle other callbacks during
CPU work.
In an event-loop server, one slow CPU-bound request harms every other request waiting for a
turn.
Correct Answer:
Explanation
The Chef (call stack) is stuck computing AI moves for 2 seconds, during which every other player’s request sits in the queue. async/await can’t help because the work is CPU-bound, not I/O-bound — there’s nothing to delegate to the OS while waiting. A Worker Thread moves the heavy computation off the main thread so the Event Loop stays responsive.
Difficulty:Advanced
Arrange the lines to look up a student by ID from a roster array, handle the case where the student isn’t found, and return their data as JSON.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
.find() returns the first match or undefined, so !student is the right guard — it catches both undefined and null. The .filter() distractor returns an array, not a single object, and the === null distractor misses undefined, which is what .find() actually produces. Note Number(req.params.id) converts the string route param to match the numeric id under ===.
Difficulty:Basic
Arrange the lines to create a JavaScript object, convert it to a JSON string, parse it back, and log a property.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
JSON.stringify() converts an object to a JSON string and JSON.parse() converts it back. Plain objects have no .toJSON() method, and there is no JSON.decode() — both distractors are confusions carried over from other languages.
Difficulty:Basic
What is the value of x after this code runs?
letx;console.log(x);console.log(typeofx);
undefined means no value has been assigned; null is an explicit empty value chosen by the
program.
A declared let x; exists in scope; a ReferenceError would come from using a name that was
never declared.
JavaScript does not infer that an unassigned variable should be numeric zero.
Correct Answer:
Explanation
A declared variable with no value assigned is undefined (‘not yet assigned’), distinct from null (‘intentionally empty’) and from Python, which raises NameError for an uninitialized name. typeof undefined is "undefined"; confusingly, typeof null is "object" — a famous JS bug.
Difficulty:Advanced
Arrange the lines to safely access a nested property, provide a default, and log the result.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: const user = { profile: { address: null } };const city = user?.profile?.address?.city ?? 'Unknown';console.log(city);
Explanation
When address is null, address?.city returns undefined instead of throwing, and ?? supplies the default only for null/undefined. The || distractor would also replace 0 and empty strings with the default — wrong when those are valid values. The ternary distractor references city before it is defined.
Difficulty:Intermediate
A C++ developer writes this React component and is confused why clicking the button does nothing:
The arrow function is valid JavaScript; the problem is that changing a local variable does not
persist state or request a React render.
Arrow functions do close over surrounding variables; the issue is that the variable is recreated
on each render.
A named function would still mutate a throwaway local; the fix is to put persistent UI data in
state.
Correct Answer:
Explanation
Unlike a C++ member variable, a component function is re-invoked on every render, so each let count = 0 starts fresh and any mutation is discarded. useState stores the value in React’s own data structure that survives across renders, and calling its setter is the only thing that signals a re-render.
Difficulty:Advanced
A student stores the full filtered list in state alongside the unfiltered list: const [allTasks, setAllTasks] = useState(tasks) and const [filteredTasks, setFilteredTasks] = useState(tasks). What design problem does this create?
Storing a filtered copy creates a second source of truth that can drift from the original list.
React can render values derived from props and state during render; only data that changes
independently needs state.
A clearer variable name does not remove the bug-prone obligation to update two related states
together.
Correct Answer:
Explanation
Keep state minimal and derive everything else. The filtered list is fully computable from allTasks plus a filter string, so storing it separately creates a second source of truth that goes stale the moment one is updated without the other.
Difficulty:Advanced
Why does React require a stable key prop on list items, and why is using the array index as a key dangerous for dynamic lists?
React keys are for reconciliation identity, not CSS selection or animation by themselves.
Bad or missing keys can attach component state to the wrong item, not merely make rendering
slower.
Index keys are allowed, but they are unsafe when list items can be inserted, deleted, or
reordered.
Correct Answer:
Explanation
Keys match elements between the old and new virtual DOM trees. A stable ID lets React recognise item #42 as the same item even when its position changes; an index key only ever means “the item currently at slot 2”, so after a deletion a different item inherits that slot’s component state (checkbox, focus, input value).
Difficulty:Intermediate
In ‘Thinking in React’, why should you build a static version (props only, no state) BEFORE adding any state?
React components can use both; the teaching sequence is about reducing design complexity, not
satisfying a compiler limit.
The static-first pass is mainly about finding the minimal state model, not avoiding state
because it is inherently slow.
Building the static version first exposes component boundaries and data flow before
interactivity makes the design harder to reason about.
Correct Answer:
Explanation
A static, props-only pass settles the component hierarchy and data flow before interactivity is in the picture. With that structure visible, the minimal state is easy to spot — any data that both changes over time and can’t be computed from other state or props.
Difficulty:Advanced
What renders when count is 0?
{count&&<Badgecount={count}/>}
React suppresses false, null, and undefined, but the number 0 is valid text and will
render.
JavaScript && returns the left operand when it is falsy, so React receives 0, not <Badge
/>.
0 is not an invalid React child; it is exactly the kind of primitive React renders as text.
Correct Answer:
Explanation
&& returns its left operand when that operand is falsy, so 0 && <Badge /> evaluates to 0 — and 0 is a valid React node that renders as the text “0”. React skips false, null, and undefined, but not 0. Make the left operand a real boolean (count > 0 && ...) so it short-circuits to false.
Difficulty:Intermediate
A <SearchBar> and a <ProductTable> are sibling components. The user types in the search bar and the table should filter. Where should the filterText state live, and why?
SearchBar owns the event source, but the state must live where every dependent sibling can
receive the same value.
ProductTable uses the filter, but putting state there would leave the input sibling unable to
display and update the shared value.
A global avoids prop passing by creating hidden shared state that React cannot track through the
component tree.
Correct Answer:
Explanation
This is “lifting state up”. Both siblings depend on filterText — one to display it, one to filter rows — so it belongs in their lowest common ancestor, passed down as props (value) with a callback (onChange). The child calls onChange(newValue) to notify the parent, which re-renders both: data flows down, notifications flow up.
Difficulty:Advanced
A student proposes using class inheritance for React components: class AdminCard extends UserCard. Why does React prefer composition instead?
JavaScript supports class inheritance; React discourages it because composition fits UI
variation better.
The main issue is coupling and fragile reuse, not virtual-DOM speed.
React still understands class components, but composition is preferred over inheritance for
sharing UI structure.
Correct Answer:
Explanation
Deep inheritance hits the “fragile base class” problem — a change to the parent can silently break every subclass. Composition sidesteps it: a Card that accepts children wraps any content without knowing what it is. This is the same “prefer composition over inheritance” principle from OOP, applied to UI.
Difficulty:Advanced
Arrange the lines to build a React component with a controlled input that filters a list of items.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
Only the query is state; the filtered list is derived from items and query (minimal state), and the controlled <input> binds value to state while updating through setQuery. The useState(items) distractor stores filtered as a second, drift-prone copy; the query = e.target.value distractor mutates the variable directly and so never signals a re-render.
Difficulty:Advanced
Arrange the lines to create a custom React hook that fetches data from an API on mount.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
Custom hooks start with use. useState(null) holds the data, useEffect runs the fetch after render, and the [url] dependency array re-runs it whenever url changes. The [] distractor pins the effect to the first url (stale closure — it never re-fetches); the setData(fetch(url)) distractor stores the Promise itself instead of the resolved JSON.
Difficulty:Advanced
Arrange the fragments to write a JSX expression that conditionally renders a badge, avoiding the 0 rendering bug.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
Making the left operand a boolean (count > 0) means it short-circuits to false when count is 0, so React renders nothing. The bare {count operand would hand React the number 0, which renders as text; swapping && for || flips the logic, showing the badge precisely when count is falsy.
onClick={setCount(count + 1)} calls the setter during render instead of passing React a
function to call later.
Setters can be used from handlers; the bug is invoking the setter while JSX is being evaluated.
The code never waits for a click because the function call has already happened during render.
Correct Answer:
Explanation
onClick wants a function reference to call later, but setCount(count + 1) calls the setter right now, during render, and hands its return value (undefined) to onClick. Because that state update triggers another render, which calls the setter again, the component loops forever. Pass the function, don’t call it: onClick={fn}, never onClick={fn()}.
Difficulty:Advanced
A component fetches user data based on a userId prop:
The parent changes userId from 1 to 2, but the screen still shows user 1. Diagnose the bug.
Fetching in an effect is common; the missing dependency is what prevents the effect from
following prop changes.
Effects close over props from a render; the dependency array tells React when to create a fresh
effect with new prop values.
async/await would not change when the effect runs; [userId] is the needed data-flow
correction.
Correct Answer:
Explanation
The dependency array controls when an effect re-runs: [] fires once on mount and never again, while [userId] also re-fires whenever userId changes. Leaving userId out leaves the callback closed over the first render’s value, so it never refetches. The react-hooks/exhaustive-deps lint rule flags exactly this omission.
Difficulty:Intermediate
A component tracks a user object: const [user, setUser] = useState({ name: 'Alice', age: 25 }). How should you update only the name to 'Bob' while keeping age intact?
Mutating the same object and passing it back gives React the same reference, so change detection
and rerendering can be skipped.
Replacing the whole object with { name: 'Bob' } loses age; object spread preserves unchanged
fields.
The functional form gives the previous value safely, but returning that same mutated object
still violates React’s immutability model.
Correct Answer:
Explanation
React detects state changes by comparing references, so an update has to produce a new object. Spreading ...user into a fresh object gives that new reference while copying unchanged fields like age; mutating the existing object in place keeps the same reference and stays invisible to React.
Difficulty:Advanced
A student has four bugs in different components. Match each bug to the React concept that fixes it:
(a) Product names don’t update when different data is passed in
(b) A like counter always shows 0
(c) Deleting the 2nd item in a list causes the 3rd item’s checkbox to jump to the 2nd position
(d) A <div class="header"> renders but has no CSS styling
Incoming data changes are a props problem, while a counter that should change over time needs
state.
Keys diagnose identity moving between list items; they do not explain why incoming product data
fails to update.
JSX rules explain className, but they do not explain persistent counters or sibling data flow.
Correct Answer:
Explanation
Each symptom maps to one concept: data not updating when passed in is a props issue; a counter stuck at 0 means values aren’t in state (bare let instead of useState); state attaching to the wrong row after a delete is an index-key issue; and a class attribute with no effect is JSX (class → className).
Difficulty:Intermediate
Arrange the lines to add an item to a shopping cart stored in React state, using immutable updates.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The functional update setCart(prev => [...prev, product]) builds a new array — previous items plus the new one — giving React a fresh reference to re-render on. The cart.push(product) distractor mutates in place, and setCart(cart) hands back the same reference; React detects changes by reference, so it ignores both.
Difficulty:Advanced
Arrange the lines to build a counter component that safely increments using the functional update pattern.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The functional form setCount(prev => prev + 1) asks React for the latest state at update time, so batched clicks don’t read a stale value. The count = count + 1 distractor reassigns a local variable and never re-renders; the onClick={handleClick()} distractor calls the handler during render instead of registering it, which fires the setter every render and loops forever.
Difficulty:Advanced
Arrange the lines to build a component that fetches user data when it mounts or when userId changes, and shows a loading message while waiting.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The [userId] dependency array re-runs the fetch whenever the prop changes, and the user === null guard shows a loading message until the response arrives. The [] distractor fetches only on mount, leaving stale data when userId changes; the setUser(fetch(data)) distractor stores a Promise in state instead of the already-parsed JSON.
Difficulty:Basic
Which of the following best describes the core difference between centralized and distributed version control systems (like Git)?
Git can commit and inspect history locally because each clone has the repository history, even
if teams later share through a remote.
A single source of truth can be useful, but distributed history is precisely what made large
open-source collaboration practical.
Git branching is cheap because branches are lightweight refs to commits, not because a central
server serializes all work.
Correct Answer:
Explanation
Distributed VCS gives every developer a full local copy of the entire repository and its history. Because the whole history lives locally, you can commit, branch, and inspect offline and work independently; a shared remote is a convention, not a requirement.
Difficulty:Basic
What are the three primary local states that a file can reside in within a standard Git workflow?
Untracked, tracked, and ignored describe how Git classifies files; they are not the three places
changes move through before a commit.
Branches and remotes name histories and sharing locations; they are not local areas holding a
file version.
Git’s local workflow is about working tree, index, and committed snapshots, not upload/download
status.
Correct Answer:
Explanation
The three local areas are the Working Directory (files on disk), the Staging Area/index (the next commit being assembled), and the Local Repository (committed history).git add moves a change from the working directory into staging; git commit turns staging into a new snapshot in the repository.
Difficulty:Intermediate
What does the command git diff HEAD compare?
git diff without HEAD compares working tree to the index; adding HEAD compares against the
latest commit snapshot.
HEAD is local; comparing to a remote requires naming a remote ref such as origin/main.
Comparing the latest commit to its parent is a history inspection task, not what git diff HEAD
does to uncommitted work.
Correct Answer:
Explanation
git diff HEAD compares the working directory (including staged changes) against the snapshot of the latest commit. Plain git diff stops at the index; adding HEAD reaches past it to show every uncommitted change, staged or not.
Difficulty:Basic
Which Git command should you NEVER use on a shared branch because it can permanently overwrite and destroy work pushed by other team members?
git pull can create conflicts or merge commits, but it does not overwrite shared history the
way a force push can.
git fetch downloads remote objects and updates remote-tracking refs locally; it does not
publish or delete teammates’ commits.
Squashing changes commit shape during integration, but it is not the command that overwrites an
existing remote branch ref.
Correct Answer:
Explanation
git push -f forces the remote branch ref to match your local history, so any teammate commits not in your local copy are dropped from that branch. Those commits may still be recoverable from another clone or the reflog, but anyone who based work on the old branch is now disrupted — which is why force-pushing is reserved for branches only you use.
Difficulty:Intermediate
Which of the following are advantages of a Distributed Version Control System (like Git) compared to a Centralized one? (Select all that apply)
Offline commits and history inspection are central benefits of a distributed repository, not
conveniences added by hosting services.
A full local history is what lets developers branch, inspect, and recover work without constant
server access.
Distribution changes where history lives; conflicting edits to the same lines can still happen
and still require resolution.
Git teams often use a central remote socially, but the VCS model does not strictly rely on one
server for all metadata.
Large open-source projects benefit because contributors can work independently and exchange
history without a single write bottleneck.
Correct Answers:
Explanation
A full local copy of history gives every developer offline commits and removes the central single point of failure, which is what scales open-source collaboration. Distribution changes where history lives, not whether edits collide — two people changing the same lines still produce a merge conflict.
Difficulty:Basic
Which of the following represent the core local states (or areas) where files can reside in a standard Git architecture? (Select all that apply)
The working directory is where editable files live before Git snapshots them.
The staging area is Git’s proposed next snapshot, which is why staging can differ from both disk
and the last commit.
A remote server may store repository history, but it is not one of the three local areas on the
developer’s machine.
The local repository is where committed snapshots and refs are stored.
Detached HEAD describes what HEAD points to, not a place where file contents reside.
Correct Answers:
Explanation
The three local areas are Working Directory (files on disk), Staging Area (the next commit being assembled), and Local Repository (the compressed commit history). A remote server is external infrastructure, and detached HEAD describes what HEAD points at — neither is a place file contents reside.
Difficulty:Intermediate
Which of the following commands are primarily used to review changes, history, or differences in a Git repository? (Select all that apply)
git log answers what happened in the commit graph, so omitting it misses a core review tool.
git diff is the command for comparing file content across Git states.
git show displays the content and metadata of a particular object such as a commit.
git push publishes local history to a remote; it is not primarily a review command.
git init creates repository metadata; it does not review existing changes or history.
Correct Answers:
Explanation
git log, git diff, and git show are the inspection commands: history, differences between states, and the details of one object respectively.git push uploads local history to a remote and git init creates a repository — neither reviews existing changes.
Difficulty:Advanced
A faulty commit was pushed to a shared ‘main’ branch last week and your teammates have already synced it. Why should you use git revert to fix this rather than git reset --hard followed by a force-push?
git revert may still need conflict resolution; its safety comes from preserving shared
history.
Revert creates a new inverse commit rather than moving old changes back into the index for
editing.
Revert does not remove the bad commit; it records a later commit that cancels its changes.
Correct Answer:
Explanation
git revert is safe on shared branches because it adds a new forward commit that cancels the bad one, leaving existing history intact.reset --hard plus a force-push instead overwrites the shared branch, disrupting every teammate who already synced the old history.
Difficulty:Advanced
When integrating a feature branch into ‘main’, under what condition will Git perform a fast-forward merge rather than creating a three-way merge commit?
Squashing changes the number of commits being integrated; fast-forward depends on whether the
target branch has diverged.
Conflicting edits indicate divergent work; a fast-forward has no merge to reconcile.
--squash stages a combined change for a new commit; it is not the condition that lets Git
slide a branch pointer forward.
Correct Answer:
Explanation
A fast-forward merge is possible only when the base branch has gained no new commits since the feature branch diverged, so Git can slide its pointer forward linearly. If both branches have unique commits since the split, Git instead builds a three-way merge using their common ancestor, producing a merge commit with two parents.
Difficulty:Advanced
Arrange the Git commands into the correct order to: create a feature branch, make changes, and integrate them back into main via a merge.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The correct order is: create the branch, stage changes, commit, switch back to main, then merge — force-push and init are distractors irrelevant to a local merge workflow. The workflow is: create and switch to a feature branch (switch -c), stage changes (add), commit them, switch back to main, and merge the feature branch in. git push -f is dangerous on shared branches and is not part of a local merge workflow. git init creates a new repository — irrelevant here.
Difficulty:Advanced
Arrange the commands to undo a bad commit on a shared branch safely: first identify the commit, then revert it, then push the fix.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The correct order is log → revert → push: find the bad commit, add an inverse commit that cancels it, then share the fix — keeping shared history intact. The distractors reset --hard and push -f rewrite the shared branch and destroy teammates’ work. (On a busy branch, git pull before git push, or the push may be rejected as non-fast-forward.)
Difficulty:Intermediate
Arrange the commands to initialize a new repository and record an initial commit.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The correct order is init → add → commit: create the repository, stage all files, then record the first snapshot — clone copies an existing repo and push requires a remote to already exist.git init creates the repository, git add . stages every file in the working directory, and git commit records the first snapshot. git clone copies an existing remote repo — it doesn’t create one from scratch. git push requires a remote to already exist.
Difficulty:Intermediate
Arrange the commands to register a remote called origin and push the main branch to it for the first time.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The correct order is remote add → push -u: register the remote alias first, then push with -u to set the upstream tracking reference — fetch and pull download data and cannot publish a local branch.git remote add origin <url> registers the remote address under the alias origin. git push -u origin main uploads the branch and sets the upstream tracking reference so future git push/git pull calls need no extra arguments. git fetch and git pulldownload data — they don’t publish your local branch.
Difficulty:Intermediate
You have some uncommitted, incomplete changes in your working directory, but you need to switch to another branch to urgently fix a bug. Which command is best suited to temporarily save your current work without making a messy commit?
Cherry-pick copies an existing commit onto the current branch; it does not temporarily save
uncommitted work.
Bisect searches history for the commit that introduced a bug; it is not a work-in-progress
storage tool.
Revert creates a new commit that undoes an old commit, which is the opposite of keeping
incomplete work out of history.
Correct Answer:
Explanation
git stash shelves your staged and unstaged changes onto a private stack and leaves a clean working tree, so you can switch contexts without committing half-finished work. Plain git stash ignores untracked files; add -u to include them. Restore with git stash pop.
Difficulty:Intermediate
What happens when you enter a ‘Detached HEAD’ state in Git?
Detached HEAD is a pointer state, not an automatic merge conflict.
Checking out a commit directly does not delete main; it only makes HEAD stop following a
branch name.
Detached HEAD is local repository state and says nothing about whether a remote server is
online.
Correct Answer:
Explanation
Checking out a commit by its hash points HEAD directly at that commit instead of a branch — “detached HEAD.” New commits made here advance no branch pointer, so once you switch away nothing references them and they can be garbage-collected.
Difficulty:Intermediate
Which Git command utilizes a binary search through your commit history to help you pinpoint the exact commit that introduced a bug?
git blame shows the last commit touching each line, but it does not run a binary search over
good and bad revisions.
git diff compares two states; it does not manage the iterative good/bad testing process.
Cherry-pick applies one known commit elsewhere; it is not for discovering which commit was bad.
Correct Answer:
Explanation
git bisect binary-searches commit history to pinpoint the commit that introduced a bug. You mark one known-good and one known-bad commit; Git checks out the midpoint and you test, repeatedly halving the range until one culprit commit remains — log(n) tests instead of n.
Difficulty:Intermediate
What is the primary purpose of Git Submodules?
Submodules track another repository at a chosen commit; they do not partition large files for
performance.
A submodule is versioned source history, not a credential vault.
Squashing rewrites or combines commits; submodules preserve an external repository’s independent
history.
Correct Answer:
Explanation
A submodule embeds an external repository as a subdirectory, pinning it to one specific commit while its history stays independent. The outer repo stores only that pinned SHA, not a copy of the files — useful for sharing a library across projects without vendoring its code.
Difficulty:Advanced
In which of the following scenarios would using git stash be considered an appropriate and helpful practice? (Select all that apply)
Stash is designed for this exact temporary pause: keep unfinished edits without creating a
misleading commit.
Stashing before pulling avoids mixing local unfinished edits with incoming changes.
Stash stores working changes temporarily; it does not remove files from project history.
Applying a completed commit from another branch is cherry-pick territory, not stash.
Correct Answers:
Explanation
git stash shelves staged and unstaged modifications and leaves a clean working tree — ideal for a quick context switch or a clean pull, not for deleting files or moving commits. It saves work temporarily; it never removes files from history, and applying a finished commit from another branch is git cherry-pick’s job.
Difficulty:Advanced
Which of the following are valid methods or strategies for integrating changes from a feature branch back into the main codebase? (Select all that apply)
A merge preserves the branch topology while bringing feature changes into the target branch.
Rebasing can integrate work by replaying commits onto a new base, with the tradeoff that commit
identities change.
Squash merge integrates the final content as one new commit rather than preserving every
feature-branch commit.
git bisect identifies a bad commit; it does not combine branch histories.
git blame attributes lines to commits; it does not move changes between branches.
Correct Answers:
Explanation
Merging, rebasing, and squash-merging are the three integration techniques — git bisect and git blame are inspection tools, not integration strategies. One caveat: git merge --squash only stages the combined changes, so it must be followed by git commit to create the squashed commit.
Difficulty:Advanced
What does the file .git/HEAD contain when you are checked out on a branch, compared to when you are in a detached HEAD state?
On a branch, .git/HEAD usually points to the branch ref; the branch ref points to the commit
hash.
Detached HEAD changes the pointer representation to a raw hash; Git does not store the state as
the same hash plus warning text.
HEAD identifies the current commit or branch ref, while the staging area is stored separately
as the index.
Correct Answer:
Explanation
On a branch, .git/HEAD holds a symbolic reference like ref: refs/heads/main (a pointer to a branch pointer); in detached HEAD it holds a raw commit SHA directly. Detaching (e.g. git switch --detach <hash>) disconnects HEAD from every branch, so commits made there advance no branch pointer and can be lost on switching away.
Difficulty:Advanced
Arrange the commands to safely stash your work, pull remote changes, and restore your stashed work.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
→ Drop here →
Correct order: git stash&&git pull&&git stash pop
Explanation
The correct order is stash → pull → pop: shelve local changes first, then pull onto a clean working directory, then restore — drop discards the stash and reset –hard destroys local work.git stash saves uncommitted changes temporarily, git pull fetches and merges remote changes onto a clean working directory, and git stash pop re-applies the stashed changes on top. git stash drop would discard the stash without applying it. git reset --hard would destroy all local changes — dangerous!
Difficulty:Advanced
Arrange the commands to stage a forgotten file and fold it into the last commit without changing the commit message.
Drag fragments into the answer area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
The correct order is add → amend –no-edit: stage the forgotten file, then fold it into the last commit while keeping the original message.reset --hard HEAD~1 would destroy that commit, and a fresh commit -m adds a fixup commit that clutters history — amend is cleaner as long as the commit hasn’t been pushed.
new String("hello") creates two separate objects, so reference equality is false even though
the characters match.
The two objects have the same character content. .equals() is designed to compare that content
for String.
== compares references for objects; it does not use string interning to make two explicit new
String(...) objects equal. .equals() is the content comparison.
Correct Answer:
Explanation
new String("hello") forces a fresh heap object each time, bypassing the string intern pool. So a and b are different objects: == compares references and returns false, while .equals() compares character content and returns true. Never compare strings with == — always use .equals().
Autoboxing reuses some boxed integers, but not all of them. The standard cache covers small
values such as -128 through 127, not 200.
== can be applied to Integer references. The dangerous part is that it compares object
identity rather than numeric value.
== does not generally compare boxed integer values. .equals() gives the numeric value
comparison here.
Correct Answer:
Explanation
Java caches Integer objects only for values −128 to 127. Outside that range, autoboxing 200 calls Integer.valueOf(200), which allocates a new object each time, so x and y are distinct objects: == returns false while .equals() returns true. Always use .equals() for wrapper types.
Difficulty:Intermediate
What happens at runtime when this code executes?
Integercount=null;intn=count;
Java does not use 0 as the value of a null boxed integer. Unboxing needs an actual Integer
object to call.
Assigning an Integer to an int is allowed through auto-unboxing. It fails at runtime here
because the reference is null.
Java has no -1 null sentinel for primitive int. The unboxing operation throws before any
primitive value can be assigned.
Correct Answer:
Explanation
Auto-unboxing is syntactic sugar for .intValue(), so assigning a nullInteger to an int calls .intValue() on null and throws NullPointerException. This is a common production bug: it works in testing with small non-null values and explodes when a database returns null. Always check for null before unboxing.
Difficulty:Advanced
A teammate writes this in a hot loop:
Integersum=0;for(inti=0;i<1_000_000;i++){sum+=i;}
You suggest changing Integer sum to int sum. What is the precise reason?
The JIT may optimize some cases, but relying on it hides the real cost model. Integer
arithmetic can create boxing and unboxing overhead that primitive int avoids.
Integer wraps a 32-bit int, not a 64-bit value. The performance issue is boxing, not integer
width.
The compound operator is legal with Integer because Java unboxes and reboxes. That automatic
conversion is exactly the cost being avoided.
Correct Answer:
Explanation
On an Integer, sum += i expands to sum = Integer.valueOf(sum.intValue() + i) — two method calls and one heap allocation per iteration. In a million-iteration loop that is a million short-lived objects and enormous garbage-collector pressure, which primitive int avoids entirely. Use int for accumulators, counters, and any variable never placed in a generic container.
Difficulty:Advanced
In Java, what is the default access level when no access modifier is specified on a field or method?
private is the default in a C++ class, not in Java. Java’s no-modifier access is
package-private — wider than private.
Java does not make unmarked members public. Public access requires the explicit public
modifier.
Protected access includes subclasses and package access, but default Java access is
package-private only.
Correct Answer:
Explanation
Java’s default (no modifier) is package-private — any class in the same package can read or write the member. This is a false friend from C++, where the default in a class is private, and a common source of accidental data exposure on transition. Always be explicit with Java access modifiers.
Difficulty:Advanced
A GradeReport class has private ArrayList<Integer> scores and exposes it like this:
All fields are private. Has information hiding (Parnas) been achieved?
Private fields are only part of the story. Returning ArrayList<Integer> exposes the
representation choice through the public API.
Encapsulation and information hiding are related but not identical. A field can be private while
the design decision behind it is still exposed.
The ArrayList itself is not hidden when it appears in the method signature. Callers can now
depend on list-specific behavior.
Correct Answer:
Explanation
Encapsulation (private fields) and information hiding are orthogonal: the field is encapsulated, but the return type ArrayList<Integer> leaks the secret — the storage choice — through the public API, so switching to int[] or a database breaks every caller. A properly hidden design exposes behavior instead — getAverage(), getLetterGrade(int score), formatReport() — so callers never see how scores are stored.
Difficulty:Intermediate
Dog, Car, and Printer each need a serialize() method. They share no fields or common behavior. Which Java construct is the right fit?
An abstract class is best when related classes share implementation or state. These classes only
share a capability.
A concrete base class would create an artificial inheritance relationship among unrelated types.
The shared idea is a contract, not a common ancestor with state.
Generics parameterize types; they do not by themselves define a shared method contract for
unrelated classes.
Correct Answer:
Explanation
Interfaces define a contract for unrelated classes that share behavior but no state — exactly the case for Dog, Car, and Printer, mirroring Java’s own java.io.Serializable. An abstract class fits only when related classes also share fields and concrete methods; if these three shared, say, a createdAt field and a log() implementation, an abstract class would be appropriate.
Difficulty:Advanced
Why is ArrayList<int> illegal in Java, while vector<int> is valid in C++?
ArrayList can store any reference type, not only String and Object. The problem is that
int is primitive, not a reference type.
There is no import that makes Java generics accept primitives directly. Use wrapper types such
as Integer.
Java does not silently rewrite ArrayList<int> into ArrayList<Integer>. The source type
argument must already be a reference type.
Correct Answer:
Explanation
C++ templates generate separate code per instantiation, so vector<int> and vector<string> are distinct compiled types. Java instead uses type erasure: there is one compiled ArrayList class and type parameters become Object after compilation. Since int is not an Object subtype it cannot be a type parameter — wrap it with Integer, and autoboxing handles the conversion.
instanceof works with classes and interfaces. The rejected part is the parameterized type
List<String> after erasure.
A cast cannot recover erased generic element types. The JVM still cannot know whether a runtime
List was intended as List<String>.
instanceof List<?> is valid. The issue is asking about the erased element type, not checking
an interface.
Correct Answer:
Explanation
After type erasure the JVM cannot distinguish List<String> from List<Integer> — both are just List — so instanceof List<String> is an unsound check the compiler rejects. You can write instanceof List<?> (unbounded wildcard) to test whether something is any list, but you lose the element-type information.
Difficulty:Intermediate
Match each task to the best collection:
A: Track which students have submitted homework (no duplicates, O(1) lookup by name)
B: Map each student ID (int) to their final grade (double)
C: Maintain an ordered history of grade submissions (newest at the end, access by index)
ArrayList preserves order, but membership checks are linear and duplicates need manual
prevention. That does not fit fast lookup with no duplicates.
A HashMap maps keys to values; it is not the natural representation for task A’s set
membership or task C’s ordered history.
A HashMap does not preserve an ordered grade-submission history by index. It is for
key-to-value lookup.
Correct Answer:
Explanation
Each task matches one collection’s contract: HashSet<String> gives O(1) contains() with automatic deduplication (A), HashMap<Integer, Double> maps IDs to grades (B), and ArrayList<Double> keeps insertion order with O(1) index access (C). Using ArrayList for the submission tracker would force O(n) contains() checks and manual duplicate prevention.
HashMap fully supports String keys. The failure happens when a missing key produces null
and Java tries to unbox it.
With generics, scores.get("Bob") has type Integer, not raw Object. Auto-unboxing is
allowed but fails on null.
HashMap.get() returns null for a missing key, not 0. A default value requires
getOrDefault() or explicit handling.
Correct Answer:
Explanation
HashMap.get() returns null for a missing key, and auto-unboxing that null to int calls .intValue() on null, throwing NullPointerException. Fix with scores.getOrDefault("Bob", 0), or check containsKey("Bob") first. This is one of the most common Java production bugs.
Difficulty:Basic
Which exceptions does the Java compiler force you to explicitly catch or declare with throws?
Java does not force handling for every exception. Unchecked runtime exceptions are not
compiler-enforced.
Unchecked exceptions can be caught, but the compiler does not force callers to catch or declare
them.
Compiler enforcement is based on exception type hierarchy, not whether the exception was
user-defined.
Correct Answer:
Explanation
The compiler enforces only checked exceptions — subclasses of Exception excluding RuntimeException (IOException, SQLException). These model recoverable external failures the caller must decide how to handle. Unchecked exceptions (NullPointerException, IllegalArgumentException) model programming errors that shouldn’t occur if the code is correct, so the compiler doesn’t force handling.
Difficulty:Advanced
In a Java constructor, where must super(args) appear, and what happens if you omit it?
Java requires constructor chaining before the subclass body runs. It cannot insert super()
after other statements.
Parent construction must happen before subclass field setup in the constructor body.
super(...) or this(...) must come first.
A superclass constructor is always invoked. If no explicit call is written, Java tries to insert
super(), which can fail if no no-arg constructor exists.
Correct Answer:
Explanation
Java requires super(...) or this(...) to be the first statement of a constructor, so the parent is fully constructed before subclass code runs. Omitting it makes Java insert a no-arg super(); if the parent defines only parameterized constructors, that implicit call fails and you get a compile error. C++ expresses the same ordering through initializer lists: Car(args) : Vehicle(make, year) { }.
Vehicle is abstract with abstract describe(). Car overrides it. Which describe() runs?
The reference type controls what methods are legal to call, but the runtime object type controls
which overridden implementation runs.
Calling an abstract method through a superclass reference is legal when the actual object is a
concrete subclass implementing it.
No cast is needed to dispatch an overridden method. Dynamic dispatch is the normal Java method
call behavior.
Correct Answer:
Explanation
Java methods are virtual by default — unlike C++, which needs the virtual keyword — so the JVM dispatches on the actual object type (Car) at runtime, not the declared reference type (Vehicle). That is polymorphism: one line, v.describe(), calls the right implementation for each object in a heterogeneous collection. @Override confirms the intent at compile time.
Difficulty:Advanced
Arrange the lines to implement a generic Pair<A, B> class with a static swap method that returns a Pair<B, A>.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: public class Pair<A, B> { private A first; private B second; public Pair(A first, B second) { this.first = first; this.second = second; } public A getFirst() { return first; } public B getSecond() { return second; } public static <X, Y> Pair<Y, X> swap(Pair<X, Y> p) { return new Pair<>(p.getSecond(), p.getFirst()); } }
Explanation
Because swap is static, it cannot use the class’s <A, B> and must declare its own type parameters <X, Y>, returning Pair<Y, X> to actually flip the types. The Pair<X, Y> return-type distractor swaps nothing; the raw Pair distractor loses all type safety. (Java’s private is class-scoped, so reading p.second from inside Pair would compile, but getSecond() matches the surrounding style.)
Difficulty:Intermediate
Arrange the lines to define a Shape interface and a Circle class that correctly implements it.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: public interface Shape { double getArea(); double getPerimeter(); } public class Circle implements Shape { private double radius; public Circle(double radius) { this.radius = radius; } @Override public double getArea() { return Math.PI * radius * radius; } @Override public double getPerimeter() { return 2 * Math.PI * radius; } }
Explanation
A class uses implements for an interface (extends is for class inheritance), interface methods are signatures with no body, and the class must provide all of them. @Override is optional but recommended — the compiler verifies a real method is being overridden, catching typos. The abstract-class distractor compiles but is wrong for a pure contract with no shared state.
Difficulty:Advanced
Arrange the lines to define a checked exception, declare it in a method, and handle it in calling code.
Drag lines into the solution area in the correct order (some items are distractors that should not be used). Keyboard: focus a line and press Space or Enter to move it between the bank and the answer area. Use Arrow Up or Arrow Down to reorder within the answer area.
↓ Drop here ↓
Correct order: class InsufficientFundsException extends Exception { public InsufficientFundsException(String msg) { super(msg); } } public boolean withdraw(double amount) throws InsufficientFundsException { if (amount > balance) { throw new InsufficientFundsException("Insufficient funds"); } balance -= amount; return true; } try { account.withdraw(1000.0); } catch (InsufficientFundsException e) { System.out.println("Error: " + e.getMessage()); }
Explanation
A checked exception extends Exception (not RuntimeException), and the method’s throws declaration is mandatory — callers that neither catch nor re-throw it won’t compile. The RuntimeException distractor would make it unchecked, removing that enforcement. Omitting throws is itself a compile error once the body contains throw new InsufficientFundsException(...).
Difficulty:Expert
You’re designing a Course class. It needs:
A way for other classes to enroll/drop students without knowing the internal storage
Fast O(1) lookup for isEnrolled(String name)
No duplicate enrollments
Which two decisions together best achieve these goals?
Exposing getStudents() leaks the storage decision and makes duplicate prevention a caller
problem. It also gives List lookup costs.
A HashMap may support lookup, but public fields destroy the information-hiding requirement and
expose representation directly.
Returning the full ArrayList makes callers depend on the internal collection and gives linear
lookup plus manual duplicate checks.
Correct Answer:
Explanation
The Enrollable interface decouples the contract from the implementation, so callers depend on behavior rather than on how students are stored (information hiding). LinkedHashSet<Student> gives O(1) contains(), automatic deduplication, and insertion-order iteration — all three requirements at once — where ArrayList would need O(n) duplicate checks and exposing getStudents() would leak the storage decision.
Difficulty:Basic
In C, what is the difference between 'a' and "a"?
Python lets you swap quote styles freely, but C distinguishes them: single quotes produce a
single char, double quotes produce a null-terminated char array.
C has no built-in string object — "a" is just memory containing the byte 'a' followed by
'\0'. String functions in <string.h> walk that array until they hit the terminator.
Every C string literal ends with the null byte '\0'. Library functions like strlen and
strcmp rely on it; without the terminator they would read past the array.
Correct Answer:
Explanation
'a' is a single char; "a" is a two-byte char array — the letter plus the null terminator '\0'. This distinction is enforced by the type system: passing 'a' to a function expecting char* is a type error, and the string-library functions in <string.h> rely on the trailing '\0' to know where the string ends.
Difficulty:Intermediate
C does not support function overloading. If you want both int and float versions of a print function, what does the standard C convention look like?
C does not mangle names; the linker sees raw symbols and rejects duplicates. That is precisely
why C symbols are callable from any language without runtime metadata.
C pointers do not carry runtime type information. Inspecting the bytes would require an extra
tag the caller has to pass, which is exactly what unique names already encode.
_Generic (C11) does enable type-keyed dispatch through a macro, but everyday C code follows
the simpler convention of distinct names like printInt and printFloat.
Correct Answer:
Explanation
Every C function in a translation unit needs a unique name, so the convention is printInt, printFloat, etc. This is why the C standard library has families like abs / fabs / labs instead of overloads, and why printf uses format specifiers (%d, %f) instead of taking arbitrary argument types. Avoiding name mangling is also what makes C symbols straightforward to call from Python, Java, Rust, and almost every other language.
Difficulty:Intermediate
A C++ programmer wants to translate this swap function to C:
What is the correct C version, including the call site?
C is pass-by-value: a function gets a copy of every argument it receives. To let it mutate a
caller’s variable, the caller must pass a pointer and the function must dereference it.
C99 added many features but not references. The & symbol in C means “address-of” at a
use site and bitwise-AND as an operator — never the pass-by-reference declarator.
Single indirection is enough — a single pointer lets the function read and write the caller’s
int. Double indirection is only needed to change what the caller’s pointer itself points to.
Correct Answer:
Explanation
C has no references — the function takes int* parameters and the caller writes &x, &y at the call site. Every signature in C therefore tells you whether a function can mutate its argument: a value parameter cannot; a pointer parameter might. C++ references hide this at the call site, which is more convenient but less explicit about who can change what.
Difficulty:Advanced
A C function int safe_divide(int num, int den, int* result) returns 0 on success and -1 on division by zero. Which call site uses this contract correctly?
safe_divide writes the quotient through the pointer; passing NULL would cause it to
dereference a null pointer and segfault.
Ignoring the return value of an error-coded C function defeats the entire convention. On
division by zero z is left uninitialized and the printf reads garbage.
Mixing the return value and the output pointer like this is undefined behavior — z would be
read on the right-hand side before it is written, even on success.
Correct Answer:
Explanation
The output-pointer convention requires passing the address of a result variable and checking the integer return code on every call. Functions that report errors through return codes only work when callers actually check them. Forgetting the check is one of the most common bugs in C code — it leaves the program running on uninitialized memory after a silent failure.
Difficulty:Advanced
Consider this C code:
int*arr=malloc(10*sizeof(int));free(arr);arr[0]=42;// Line Afree(arr);// Line B
What is the most likely consequence?
free immediately returns the block to the allocator; the program does not wait until exit.
After free the address may be reused by the next malloc.
The C compiler does not track freed pointers — that would require runtime bookkeeping the
language deliberately avoids. Both errors slip past the compiler and surface at runtime.
free does not modify the pointer variable or zero the released memory. The pointer keeps
pointing at the (now invalid) address — which is exactly what makes use-after-free dangerous.
Correct Answer:
Explanation
Use-after-free and double-free are both undefined behavior — they can crash, corrupt other allocations, or appear to work and fail later. A common defensive habit is to write free(arr); arr = NULL; after every free. Subsequent writes through a NULL pointer fail loudly with a segfault instead of silently corrupting whatever the allocator handed to the next caller.
Difficulty:Intermediate
What is the role of libc (the C standard library) in a typical C program?
C is compiled to native machine code; there is no interpreter or bytecode VM at runtime. libc
is an ordinary library linked into your executable, not a runtime engine.
Browser JavaScript is sandboxed precisely so it cannot call into native C. libc has no role
here — for browser code you would compile C to WebAssembly instead.
Compiling source to object files is the compiler’s job, not the library’s. libc is the
collection of functions the linker binds your object files against.
Correct Answer:
Explanation
libc is the portability layer between your C source and the OS — same API everywhere, different syscalls underneath. Each operating system ships its own implementation (glibc on Linux, libSystem on macOS, MSVCRT on Windows). The linker selects the right one for your target platform at build time, which is what makes a single C source compile to working executables on three different operating systems.
Difficulty:Advanced
Dijkstra’s note “Go To Statement Considered Harmful” effectively retired goto from mainstream programming, yet the C language still has it and the Linux kernel uses it heavily. Which use of goto is widely accepted in modern C style guides?
Crossing loop boundaries with goto makes the control flow exactly the kind of jungle gym
Dijkstra warned about. Refactoring the inner loop into its own function is cleaner.
C’s goto only targets labels in the same function — the language does not let you jump
across function boundaries. Inter-function jumps are what function calls are for.
C has while, for, and do/while for structured looping. Hand-rolled loops via goto
bypass the readability the structured-programming movement was built around.
Correct Answer:
Explanation
Forward goto to a single per-function cleanup label is the accepted idiom — every error path frees resources by jumping to one place, avoiding nested if pyramids or duplicated cleanup code. This is what the Linux kernel does throughout. The pattern stays structured because every jump goes forward to one label; it never simulates loops, cross-function jumps, or unbounded control flow.
Difficulty:Expert
NASA’s coding standards for flight software permit C and a restricted subset of C++ — explicitly forbidding exceptions and most polymorphism. What is the strongest pedagogical reason for that restriction?
Modern C++ compilers can match C performance on the same code path. The verification problem
is the binding constraint — predictability of control flow, not raw speed.
Source-file size is not the relevant constraint. Flight computers run compiled machine code,
and many modern aerospace platforms have ample storage for either language’s source.
C++ has been mature for decades and has multiple production-grade compilers. The restriction
is about the language’s semantics, not the compilers’ maturity.
Correct Answer:
Explanation
Polymorphism and exceptions introduce hidden control-flow edges that automated verification can’t easily reason about — and you cannot debug a Mars rover. Every line of code inside a try block has an implicit edge to a catch handler; every virtual call may dispatch to any subclass override. Tools that prove safety properties have to reason about all of those edges; banning them keeps the control-flow graph small and analyzable.
Difficulty:Advanced
Almost every mainstream language can call into a C library — Python, Java, C#, Rust, Go, Ruby — but browser JavaScript cannot directly call C functions on the user’s machine. What is the strongest reason?
Server-side JavaScript (Node.js) does have FFI options (N-API, ffi-napi). The constraint is
specific to the browser sandbox, not to the language.
Engines can describe ABIs and decode binary returns — Wasm and Node FFI both do this. The
blocker is the security boundary, not the data layout.
Interpreters routinely call into compiled native code — every JavaScript engine itself does
this constantly. The browser blocks JS→C calls by design, not because it can’t make them.
Correct Answer:
Explanation
Browser JavaScript is sandboxed precisely so a page cannot touch the filesystem, hardware, or memory outside the engine — and C has unrestricted access to all of those. WebAssembly is the modern workaround: C compiled to Wasm runs in the same sandbox as JavaScript, with no privileged hardware access. The sandbox is not a limitation of the language but a deliberate security boundary.
Difficulty:Advanced
You are shipping a CLI tool that depends on libssl. Compare static and dynamic linking — which statement is correct?
Static linking happens at build time and produces a self-contained binary. Dynamic linking
defers resolution until program startup, which is what creates the runtime dependency.
The two produce different artifacts: a static binary contains the library code; a dynamic
binary contains references that are resolved at load time against shared libraries on the
system.
It is the other way around — static linking copies library code into the executable, and
dynamic linking is what splits responsibility between the executable and external .so /
.dll files.
Correct Answer:
Explanation
Static linking trades binary size for portability and update independence; dynamic linking trades a runtime dependency for smaller executables and shared security updates. A pure-static binary will run on any compatible OS without external libraries. A dynamic binary needs the right shared libraries present at startup, but a single libssl update on the system fixes every dynamically-linked program — you don’t have to recompile each one.
Difficulty:Basic
What is the primary mechanism make uses to determine if a target needs to be rebuilt?
Make’s default rebuild decision is timestamp-based, not content-hash-based. Some newer build
tools use hashes, but classic Make compares modification times.
Make does not ask Git which tracked files changed. It compares targets and prerequisites in the
filesystem.
Make is valuable because it avoids rebuilding work that is already up to date. Recompiling
everything is the fallback Make helps prevent.
Correct Answer:
Explanation
make rebuilds targets by comparing file modification timestamps, not hashes. If a prerequisite (like a .c file) is newer than the target (like its .o file), make recompiles. This timestamp comparison is what enables efficient incremental builds.
Difficulty:Basic
What specific whitespace character MUST be used to indent the command/recipe lines in a Makefile rule?
Spaces may look like indentation in an editor, but traditional Make syntax requires a tab for
recipe lines.
Four spaces are still spaces. Make is looking for a tab character, not a particular visual
indent width.
The colon belongs on the target/prerequisite line. Recipe lines are identified by tab
indentation on the following lines.
Correct Answer:
Explanation
A Tab character is strictly required; spaces cause a ‘missing separator’ error. Makefile syntax strictly requires command lines (recipes) to be indented with a single Tab character. Using spaces will result in a ‘missing separator’ error.
Difficulty:Basic
What does the automatic variable $@ represent in a Makefile rule?
$< is the first prerequisite. $@ is the target currently being built.
$^ expands to all prerequisites. $@ is just the output target name.
The compiler convention is usually CC. $@ is an automatic variable whose value changes for
each rule invocation.
Correct Answer:
Explanation
$@ evaluates to the target name of the current rule. It is heavily used in compilation commands (e.g., gcc ... -o $@) to specify the output file name without hardcoding it.
Difficulty:Intermediate
Why is the .PHONY directive used in Makefiles (e.g., .PHONY: clean)?
Suppressing command echo uses recipe syntax such as a leading @. .PHONY changes whether Make
treats a target name as a real file.
.PHONY does not tell the compiler to ignore errors. Recipe commands still succeed or fail
through their exit statuses.
.PHONY declares named actions such as clean. It does not discover source files or generate
object-file lists.
Correct Answer:
Explanation
.PHONY declares a target as a command, not a file, so make always runs its recipe. If you have a rule named clean, and a file literally named clean is accidentally created in the directory, make clean will say ‘clean is up to date’ and do nothing. .PHONY: clean forces make to execute the recipe regardless of whether a file named clean exists.
Difficulty:Intermediate
If a user runs the make command in their terminal without specifying a target, what will make do?
all is a convention, not a built-in default. It becomes the default only when it is the first
target.
Make does not choose the target with the most prerequisites. With no command-line target, it
starts from the first target in the file.
Running make without a target is valid. Make uses the first target as the default goal.
Correct Answer:
Explanation
make without arguments builds the very first target defined in the Makefile. By default, make begins execution with the first target it encounters in the file. By convention, developers often make the first target all (which depends on the main executables) so that running make builds the entire project.
Difficulty:Intermediate
You have a pattern rule: %.o: %.c. What does the % symbol do?
Silencing commands is controlled in recipe syntax, not by %. In a pattern rule, % matches
the shared filename stem.
% is not the current directory. It is the wildcard part that lets foo.o correspond to
foo.c.
In Make pattern rules, % is not modulo. It matches a filename stem so one rule can cover many
files.
Correct Answer:
Explanation
% is a wildcard that creates a generic pattern rule matching any .c file to its corresponding .o file. The % symbol is a wildcard in pattern rules. %.o: %.c acts as a generic template, avoiding the need to write individual rules for every single source file in a project.
Difficulty:Intermediate
Which of the following are primary benefits of using a Makefile instead of a standard procedural Bash script (build.sh)? (Select all that apply)
Incremental builds are the central benefit: unchanged targets can be skipped because Make knows
their prerequisites.
Make still runs shell commands and compilers. It saves time by running fewer commands, not by
making the compiler intrinsically faster.
The dependency graph lets Make derive a correct build order from prerequisites instead of
relying on a hand-written sequence.
Named targets such as make test and make clean give a team a shared command interface,
independent of each developer remembering the underlying commands.
Make orchestrates commands; it does not synthesize missing C or C++ code. Missing dependencies
are build inputs, not source-generation instructions by default.
Correct Answers:
Explanation
Makefiles win over shell scripts by providing incremental builds, automatic dependency ordering, and standardized commands. Makefiles save time via incremental compilation, manage complex dependency graphs automatically (declarative vs procedural), and provide standardized commands. They do not run the compiler faster, nor do they write code for you.
Difficulty:Advanced
Which of the following are valid Automatic Variables in Make? (Select all that apply)
$@ is useful because the same recipe can refer to whichever target is being built.
$< is the first prerequisite, which is why it is common in one-source-to-one-object compile
rules.
$# is a shell positional-parameter count, not a Make automatic variable.
$^ expands to the prerequisite list, which keeps generic compile and link recipes concise.
$$ is how Make emits a literal dollar sign for the shell. It is not Make’s process ID
variable.
Correct Answers:
Explanation
$@, $<, and $^ are valid Make automatic variables; $# and $$ are Bash constructs, not Make. The three valid ones keep rules concise and dynamic. $# (argument count) and $$ (a literal dollar sign for the shell) come from Bash, not Make’s automatic-variable set.
Difficulty:Advanced
In standard C/C++ project Makefiles, which of the following variables are common conventions used to increase flexibility? (Select all that apply)
CC is the standard hook for choosing the C compiler without editing the recipe body.
CFLAGS separates compile options from the rule structure, making warnings, debug flags, and
optimization settings easy to override.
LDFLAGS captures link-time options separately from compile-time options.
MAKE_IT_FAST has no standard meaning. A Make variable matters only if rules or included
makefiles actually use it.
Correct Answers:
Explanation
CC, CFLAGS, and LDFLAGS are standard Makefile conventions; MAKE_IT_FAST is not. Because tools and built-in rules recognize these names, developers can swap compilers or add flags from the command line (e.g., make CC=clang) without editing the Makefile. MAKE_IT_FAST is invented — a variable only does something if a rule actually reads it.
Difficulty:Advanced
How does the evaluation logic of a Makefile differ from a standard cookbook recipe or procedural script? (Select all that apply)
Make describes desired targets and dependencies, then decides which commands are necessary to
reach the requested goal.
A Makefile is not executed top to bottom like a shell script. Rule order mostly affects the
default target and when definitions are read.
Make plans from the requested goal down to prerequisites, then builds prerequisites before the
targets that depend on them.
Make does not run every rule blindly. Timestamp checks are specifically how it skips up-to-date
targets.
Correct Answers:
Explanation
Makefiles are declarative: they build a dependency graph top-down from the goal, then execute only necessary commands bottom-up. Unlike a Bash script, they do not run top-to-bottom or rebuild blindly — prerequisites are built before the targets that need them, and anything already up to date is skipped.
Workout Complete!
Your Score: 0/149
Shell Scripting
Shell Scripting teaches the command line as a composable programming environment: files, processes, redirection, pipes, scripting, exit codes, and regular-expression-powered text processing. The Shell Scripting Tutorial turns those ideas into hands-on practice inside a Linux environment.
Regular Expressions
Regular Expressions covers the pattern language used by tools such as grep, sed, programming-language libraries, and data-cleaning workflows. The basic RegEx tutorial introduces matching and groups, while the advanced RegEx tutorial extends the work to lazy quantifiers and lookarounds.
Python
Python is the scripting and automation language used throughout many SEBook examples, with emphasis on syntax, data structures, functions, modules, and idiomatic problem solving. The Python Essentials Tutorial builds fluency, and the Python Debugging Tutorial practices debugger-driven fault localization.
Node.js
Node.js introduces JavaScript on the server: modules, asynchronous callbacks, event loops, package management, HTTP servers, and Express-style routing. The Node.js Essentials Tutorial gives the same concepts a live coding path.
React
React teaches component-based UI construction: props, state, event handlers, conditional rendering, lists, and the mental model behind one-way data flow. The React Essentials Tutorial practices those ideas in a browser app.
Git
Git explains version control from the everyday commands up through the object model, branching, merging, rebasing, blaming, bisecting, stashing, and submodules. The Git tutorial, Advanced Git tutorial, Visual Git tutorial, and Git playground provide progressively more interactive practice.
Java
Java reviews Java for students coming from C++, Python, or JavaScript, focusing on classes, references, collections, interfaces, exceptions, and static typing. The Java tutorial provides a guided path through those differences.
C Programming
C Programming covers compilation, memory, pointers, arrays, structs, strings, and the places where C deliberately exposes machine-level details. The C for C++ Programmers Tutorial gives a focused warm-up for using C in systems and build-tool exercises.
Make and Makefiles
Makefiles and GNU Make explain dependency-based builds, targets, prerequisites, recipes, variables, automatic variables, and pattern rules. The Make and Makefiles Tutorial practices building and testing small C projects.
Playwright
The Playwright Tutorial teaches end-to-end browser testing for React applications: navigating, interacting, asserting visible behavior, designing strong user-path oracles, and using the Spec Card to plan tests before coding.
The Spec Card is a fillable planning tool for end-to-end tests. Additional focused tutorials and demos, including SQL, Prolog, and time-travel debugger demos for Python and Node.js, support narrower practice paths when a course or lesson calls for them.
Cookie & Privacy Notice:
This site stores a few preferences and your progress locally in your browser
(cookies and localStorage) so it works the way you left it.
Nothing is sent to or stored on any external server, and this site does not
sell, share, or disclose any user data to third parties.
View & manage your data →