1.5 Program Design Tools and Environments

Aligned outcomes:

SLO 1

Describe the software development life-cycle.

Design and testing are two phases of the life-cycle, and this section hands you their tools: pseudocode and flowcharts for planning before you type, and test cases plus console.log for checking what the code actually did.

SLO 2

Describe the principles of structured programming.

Structured programming's shapes show up here as pictures — the flowchart diamond is selection drawn, and indentation is nesting — alongside the sequential, parallel and recursive models an algorithm can run under.

Learning Objectives

After this section, you will be able to:

In this section, you will learn to:
  • Define computational thinking and name the four techniques it is built from.
  • Break a problem down, spot its patterns, and strip it back to what matters.
  • Write pseudocode for a task and read a flowchart's three basic shapes.
  • Run JavaScript in the browser console and in a code editor.
  • Read an error message and use console.log to find out what a program is really doing.
Video 1.5 — Think Before You Type. Three and a half minutes on computational thinking, algorithms, pseudocode and flowcharts — the design half of this section. Captions available.

1.5.1 Think Before You Type

There is a strong temptation, when given a programming problem, to start typing immediately. It feels productive. It is usually the slow way.

The reason is that a program has two separate difficulties, and typing solves only one of them:

  1. Working out what the steps are. This is thinking, and the language you use is irrelevant to it.
  2. Writing those steps in JavaScript. This is syntax, and it is much easier once step 1 is settled.

Doing both at once means every time you get confused you cannot tell which difficulty you are stuck on — the plan or the punctuation. Separating them is why this section exists. Most of the tools here are for step 1, and none of those are JavaScript.

The name for the thinking half is computational thinking.

The bridge, not the machine

A computer will do exactly what you tell it, at enormous speed, including the wrong thing. Everything that decides whether the answer is right happens before the first line of code. That is the part this section is about.

Definition 1.5.1: Computational Thinking

Computational thinking is a problem-solving process, rooted in principles from computer science, that breaks a complex problem into smaller, more manageable parts and devises systematic approaches to solve them. A complex problem is one that is difficult because it involves many different interrelated parts or factors — the kind that is hard to understand and rarely has a simple solution.

Definition 1.5.1 — Computational Thinking A two-move process panel faces a box holding seven interrelated dots. As the first move lights, three part chips appear below the tangle; as the second move lights, arrows chain those parts into an order; the chain then feeds a solution box that appears last. computational thinking 1. break it into smaller parts 2. devise a systematic approach a complex problem is one with many interrelated parts, and rarely a simple solution a complex problem part 1 part 2 part 3 a solution you can carry out all of this happens before any code is written

Definition 1.5.1 — Computational thinking is a problem-solving process, rooted in principles from computer science, that breaks a complex problem into smaller, more manageable parts and devises systematic approaches to solve them.

The phrase is still argued over, and there is no single agreed definition of it. Its value is practical rather than theoretical: it gives you separate strategies and tools you can apply to a problem that is too big to hold in your head all at once.

Computers are central to solving these problems, but a computer is only as useful as your prior understanding of the problem and its possible solutions. Computational thinking is the bridge between the problem and its resolution. It lets you navigate a complicated problem, separate its parts, and form possible solutions — solutions you can then express in a way both computers and people can follow.

Where the idea came from

Al Aho, of the Columbia University Computer Science Department, describes computational thinking as "the thought processes involved in formulating problems so their solutions can be represented as computational steps and algorithms." Jeannette Wing, also of Columbia, brought the idea to prominence in a 2006 paper written while she was at Carnegie Mellon University. Her view is that computational thinking describes the mental acts needed to compute a solution to a problem, whether a person or a machine carries it out.

A plan has no language

Section 2.2 makes this concrete: an algorithm is a plan that exists before any code does, and the same plan can be written several different ways. The tools here are how you write a plan down before it has a language.

Computational thinking gathers a collection of methods for solving hard problems — and for learning how to solve them. It sits close to mathematical thinking, because it uses abstraction, generalization, modeling, and measurement. It differs in being explicitly concerned with computation: what a machine can actually carry out, and what that buys you. Figure 1.5.1 collects the main components.

Figure 1.5.1 — Computational thinking ring Eight component skills of computational thinking arranged clockwise from the top in a ring of circles - Decomposition, Pattern recognition, Abstraction, Algorithm, Generalization, Evaluation, Modeling, Simulation - joined by gentle outward-curving connector arcs, surrounding a central ellipse labelled Computational thinking. Alternating circles are outlined in the CURVE and ACCENT tones purely to separate neighbours; the alternation carries no meaning of its own. Decom-position Patternrecognition Abstraction Algorithm General-ization Evaluation Modeling Simulation Computationalthinking

Figure 1.5.1 — This diagram illustrates the main components of computational thinking.

Critical thinking helps here more than technical skill does. It means understanding concepts rather than memorizing which menu a button lives in — comprehension over rote learning. It is a core skill rather than an extra item on a curriculum checklist, and it is uniquely human: it works on ideas rather than on things, and computers do not do it. It belongs beside reading, writing, and arithmetic.

The concepts, as ISTE lists them

The International Society for Technology in Education (ISTE) sets out the key components and dispositions of computational thinking:

Those abilities rest on some habits of mind: confidence when facing complexity, persistence with hard problems, tolerance for ambiguity, comfort with open-ended questions, and the ability to work with other people toward a shared solution.

A shorter framing is the three As, shown in Figure 1.5.2:

  1. Abstraction — formulating the problem.
  2. Automation — expressing the solution.
  3. Analysis — executing and evaluating the solution.
Figure 1.5.2 — The three As: abstraction, automation, analysis Abstraction (top, "Formation of problem"), Automation (bottom right, "Expression of solution") and Analysis (bottom left, "Execution and evaluation of solution") arranged around a ring of three curved arrows that hand off clockwise, Abstraction to Automation to Analysis and back to Abstraction. The ring surrounds a hub reading "Human abilities/ computer affordances". Each station carries an unframed schematic icon: a cluster of scattered points and short strokes flowing through an arrow into a single dot inside a small square (Abstraction); three equal ordered bars flowing through an arrow into a gear (Automation); a small lined result box beside a check mark and a cross mark (Analysis). Static figure, no motion. Human abilities/ computer affordances Abstraction Formation of problem Automation Expression of solution Analysis Execution and evaluation of solution

Figure 1.5.2 — The three As — abstraction, automation, analysis — illustrate the power of computational thinking.

The four techniques

In practice, computational thinking is usually taught as four techniques, sometimes called its cornerstones (Figure 1.5.3). They give you a systematic way to attack a problem using tools such as data structures — containers that organize and store data efficiently in a computer, defining how data is arranged and manipulated so algorithms and programs can get at it.

Figure 1.5.3 -- the four cornerstones of computational thinking A head-in-profile outline containing a solid brain shape sits at the centre. Four straight spokes reach out to four labelled corner panels -- Algorithms at top-left, Decomposition at top-right, Abstraction at bottom-left, Logical thinking at bottom-right. Each panel is a coloured title bar over a lighter body holding a small schematic icon: a mini flowchart (start circle, decision diamond, process pill) for Algorithms; two overlapping squares standing in for interlocking pieces for Decomposition; an outline cone narrowing to a solid triangle for Abstraction; a lightbulb with five rays for Logical thinking. Algorithms Decomposition Abstraction Logical thinking

Figure 1.5.3 — Users can explore the essence of computational thinking through decomposition, logical thinking, abstraction, and algorithms.

Two more steps belong alongside the four: testing, which uncovers errors in the instructions, and debugging, which finds and fixes problems in the code. Section 1.5.9 takes both up in detail.

BBC Bitesize's KS3 guide What is computational thinking? covers the same four techniques under the name cornerstones — with one difference worth noting: it splits pattern recognition out as its own cornerstone, where this book folds it into logical thinking. Its own image for the four is a table: each cornerstone is a leg, and losing any one collapses the rest.

A programmer is someone who writes instructions for a computer to follow. The standard illustration is a programmer instructing a robot to make a jam sandwich. It sounds trivial until you try it — the robot knows nothing, so every assumption you would normally skip has to be written down. The next few subsections follow that one example through all four techniques.

Try It Now 1.5.1

Before reading on, list five instructions you would give a robot to make a jam sandwich. Then hand the list to a classmate — Ethan is playing the robot, and he follows every instruction completely literally. Which one breaks first?

Solution

Answers vary, but nearly every list breaks in the same places. Ethan will usually stall at one of these:

  • "Get the bread" — from where? The robot has no idea where bread lives.
  • "Spread the jam" — with what? The knife was never picked up, and the jar was never opened.
  • "Put the bread on the plate" — which bread, and which side up?

The point is not that your list was bad. It is that ordinary instructions rely on an enormous amount of unstated knowledge, and a computer has none of it. Writing the plan down is what makes those gaps visible.

1.5.2 Decomposition and Pattern Recognition

Decomposition means solving a complex problem by breaking it into smaller, more manageable tasks. It lets you consider each component a seemingly hard task needs, until the hard task has been redefined as a set of easy ones.

Definition 1.5.2: Decomposition

Decomposition is the analytical process of breaking a complex problem or system into smaller, self-contained parts that can be understood and solved on their own.

In the jam sandwich example, decomposition means identifying every ingredient required and every step the robot must take to end up with a sandwich.

Pattern recognition then groups all those separate features into categories. For the sandwich, the things decomposition turned up sort naturally into ingredients, equipment, and actions. Applying both techniques together pushes you to think of as many things as possible — and the more you think of, the clearer the final instructions will be. A first attempt is collected in Table 1.5.1.

Table 1.5.1 — A first pass at decomposing and grouping the jam sandwich problem.
IngredientsEquipmentActions
BreadPlateRepeat x times
JamKnifeLeft hand (LH)
ButterRight hand (RH)
Pick up
Unscrew
Definition 1.5.2 — Decomposition One whole-problem box breaks into ten labelled parts, three at a time by column, then the empty box fades out and only the parts remain. The Jam Sandwich Problem Ingredients Bread Jam Butter Equipment Plate Knife Actions Repeat x times Left hand (LH) Right hand (RH) Pick up Unscrew

Definition 1.5.2 — Decomposition breaks one whole problem into smaller, self-contained parts.

Reasoning your way to a pattern

Finding patterns usually takes one of two kinds of logical thinking.

Inductive reasoning goes from specific examples to a general principle. Notice that dividing any number by 1 gives you the original number back, and you have a rule that holds for every number. Notice that the sum of two odd numbers is even, and you have another. Induction turns an observation into a pattern, which becomes a tentative hypothesis, which can become a theory.

Deductive reasoning goes the other way: drawing a valid conclusion from premises, where it is impossible for the premises to be true and the conclusion false. The traditional example is that "all men are mortal" and "Socrates is a man" force the conclusion "Socrates is mortal."

Programmers use both constantly. You use induction when three separate bugs turn out to share a cause. You use deduction when you know the input was 4, you know the function multiplies by 5, and you conclude the output should be 20 — which is exactly how you catch it printing 9.

Technology in Everyday Life: Traffic accident data

Analyzing data means collecting and cleaning information, exploring patterns with visual and statistical methods, and forming hypotheses. Statistical analysis and visualization draw the conclusions, and the findings get interpreted and communicated in a report or presentation so someone can act on them.

Categories are compression

Once "pick up," "unscrew," and "spread" are all filed under actions, you stop tracking twelve unrelated facts and start tracking three groups. That is the entire trick — pattern recognition makes a big problem small enough to hold.

Take the patterns and trends in traffic accident data: how common road injuries and fatalities are, and how incidents have changed over time. Computational thinking is what identifies the recurring patterns and abstracts the most important information out of the raw data. Extracting those insights is how the analysis turns into strategies that actually improve road safety.

Technology in Everyday Life: Computational thinking in your own life

Computational thinking is not confined to technical fields. Imagine managing your monthly expenses on a tight budget:

  1. Decomposition — break the financial problem into categories: rent, groceries, utilities, entertainment.
  2. Pattern recognition — look at past spending and find the patterns.
  3. Abstraction — focus on the few areas where costs can actually be cut.
  4. Algorithmic thinking — build a repeatable rule for allocating each month's income.

The same four steps that get a robot to make a sandwich will also get your essential costs covered while leaving something to save.

Try It Now 1.5.2

Lucía has to get to school on time tomorrow. Decompose her problem into at least six smaller tasks, then group those tasks into two or three categories of your own choosing.

Solution

One reasonable answer. Lucía's tasks: set an alarm; pick clothes; charge the phone; pack the bag; eat breakfast; check the bus time; walk to the stop; board the bus.

Grouped:

  • The night before — pick clothes, pack the bag, charge the phone, set the alarm.
  • The morning — wake up, eat breakfast, check the bus time.
  • The trip — walk to the stop, board the bus.

Any grouping that puts similar tasks together is fine. The useful discovery is that half of her tasks turn out to belong to the night before, which is the sort of thing you only see once the problem has been taken apart.

1.5.3 Abstraction

Abstraction means pulling out the important details and identifying the principles that carry over to other problems or situations. When you apply it, it often helps to write notes or draw a diagram to work out how to resolve the problem.

Definition 1.5.3: Abstraction

Abstraction is a simplified representation of a complex system or phenomenon that keeps the details relevant to the problem at hand and discards the rest.

Definition 1.5.3 — Abstraction A left column of six fact rows headed "a car, in full" beside an empty right-hand box headed "what a driver needs". Three at a time, a relevant fact is ticked and its text reappears inside the box; the paired irrelevant fact is struck through and dims. The sequence ends with three kept facts in the box and its border highlighted, and three dimmed, struck-through facts on the left. abstraction the problem at hand: driving it a car, in full what a driver needs the wheel steers it how the engine mixes fuel the pedals go and stop the gearbox ratios the gauge shows the fuel the alloy in the pistons the wheel steers it the pedals go and stop the gauge shows the fuel keep the details that change what you do -- discard the rest

Definition 1.5.3 — Abstraction is a simplified representation of a complex system or phenomenon that keeps the details relevant to the problem at hand and discards the rest.

In the jam sandwich example, abstraction means forming an idea of what the sandwich should look like. You would sketch a picture of the finished sandwich — a model of the desired outcome, with the details simplified away. Something as basic as the Windows Paint program is enough, as in Figure 1.5.4.

Figure 1.5.4 — Sandwich abstraction Two bread slices flank a jam blob, joined by plus signs, above a brace that draws down to a side view of the assembled sandwich labelled bread + filling + bread. At left, a cone (outline triangle) projecting down to a smaller solid triangle - figure 1.5.3's own abstraction icon, reused here - is labelled multiple sides put together. multiple sides put together bread + filling + bread

Figure 1.5.4 — This jam sandwich abstraction example illustrates what the final product should look like.

Layers of abstraction

Real systems stack abstractions on top of each other so that each layer only has to worry about its own job. Data in technology is represented at different levels of abstraction to keep the user's side simple while complicated operations happen out of sight.

Think about asking a generative AI tool for help. You interact through a plain interface and see none of the underlying complexity. Your prompt is then handled by the application's logic, which validates it and routes it appropriately — invisibly to you. At the back end the prompt is processed and a response is generated and returned. Each layer serves a separate role, which is what makes the whole process efficient for both you and the system (Figure 1.5.5).

Figure 1.5.5 — Abstraction layers: a GenAI round trip A front-end outline (laptop, Web framework) and a back-end outline (Language framework over Language model, joined by Input-down and Output-up arrows) sit side by side, linked by Input-left and Output-right arrows between them. Below the front end, a User sends a Prompt down and receives a Generated response up. An accent dot travels User to front end to back end to Language model, pulses in place while it processes, then retraces the same path back to the User. Front end Webframework Back end Languageframework Languagemodel Input Output Input Output User Prompt Generatedresponse

Figure 1.5.5 — When using GenAI, a user interacts with the interface while the application processes the prompt with layers of abstraction on the back end.

You already trust abstraction constantly

You drive a car without knowing how the engine mixes fuel, and you send a message without knowing how it is routed. Abstraction is not a programming trick; it is the only reason any complicated system is usable at all.

Concepts in Practice: Computational thinking in chess

Computers help us solve problems, but the problem and the shape of its solution have to be understood first. Computational thinking is not the same as programming, and it certainly is not "thinking like a computer" — computers do not think at all. Programming is the craft of telling a computer what to do; computational thinking is how you decide what those instructions should be.

Take chess. To play well, a player has to:

  • Understand the movements and strategic values of each piece, and how each one can be used to control the board.
  • Visualize the board's layout, spot threats and opportunities, and plan several moves ahead.
  • Recognize patterns from earlier games — common tactics and their counters — and build a flexible strategy from them.

Computational thinking is the framework underneath that strategy:

  • The game is dissected into smaller components — the function of each piece, the state of the board. That is decomposition.
  • Attention concentrates on the elements that decide the outcome, such as the position of key pieces and the opponent's tendencies, and sets less critical factors aside. That is abstraction.
  • Prior knowledge of similar positions becomes a step-by-step approach for playing through the game. That is algorithmic thinking.

If you ever set out to write your own chess program, these are exactly the questions you would have to settle before writing any code.

Try It Now 1.5.3

You are describing a bus route to Priya, who moved to your town last week and has never taken it. Name two details you would keep for her and two you would leave out, and say why.

Solution

Answers vary. A workable one:

Keep — the stop where Priya gets on, and the stop where she gets off. Without those the description is useless.

Leave out — the colour of the bus and the name of every street it turns down. Both are true and neither changes what she has to do.

The test for abstraction is always the same: does dropping this detail change the action the reader has to take? If not, drop it.

1.5.4 Pseudocode

An algorithm is a sequence of steps or instructions that must be followed in a specific order to solve a problem. Algorithms let you describe a solution by writing down what has to be done, and computer programs typically execute algorithms to carry out a task.

Definition 1.5.4: Algorithm

An algorithm is a finite, ordered sequence of unambiguous instructions that solves a problem or completes a task.

Definition 1.5.4 — Algorithm Four numbered robot instructions sit in a bordered panel: pick up the knife, open the jam jar, spread the jam, close the sandwich. Beside the panel, three property badges - finite, ordered, unambiguous - start unticked. A rule closes the list under its last step while "finite" ticks, the four steps then highlight in order one through four while "ordered" ticks, and a vague alternative, "spread some jam", appears below struck through while "unambiguous" ticks. A closing note reads "all three, or it is not an algorithm". an algorithm 1. pick up the knife 2. open the jam jar 3. spread the jam 4. close the sandwich and then it stops finite -- it ends ordered -- the order matters unambiguous -- one meaning spread some jam not an instruction -- two people would do different things all three, or it is not an algorithm

Definition 1.5.4 — An algorithm is a finite, ordered sequence of unambiguous instructions that solves a problem or completes a task.

In the jam sandwich example, the algorithm step is writing the instructions the robot can follow. As Chapter 3 Data Structures and Algorithms covers in more detail, algorithms are most commonly written as either pseudocode or a flowchart. Pseudocode outlines the logic using a mixture of ordinary language and high-level programming ideas, with each step in a clearly ordered written structure. A flowchart shows the flow and direction of decisions visually, as a diagram. Either is fine — it comes down to preference. Basic templates for both are shown in Figure 1.5.6.

Figure 1.5.6 — Pseudocode and flowchart map Pseudocode panel (five lines, bold keyword first) beside a Flowchart panel (Start, a Decision diamond branching True/False, Action 1 and Action 2, End). A correspondence arrow draws from each pseudocode line to its matching shape in turn, top to bottom, and accumulates rather than blinking out. Start Decision True False Action 1 Action 2 End Start If the statement is true Then go to Action 1 Else go to Action 2 End Pseudocode Flowchart

Figure 1.5.6 — Pseudocode lists each step, while a flowchart visually outlines the process of decision-making.

Pseudocode is a plan for a program written in plain language, laid out like code but not written in any programming language. There is no compiler for it and no rules to look up — the only requirement is that a human can follow it without guessing.

Here is a plan for adding up the numbers from 1 to 5:

set total to 0
set i to 1
while i is 5 or less
    add i to total
    add 1 to i
print total

Anyone who can read English can follow that, including someone who has never written a line of code. It is also close enough to a program that turning it into JavaScript later is nearly mechanical.

Definition 1.5.5: Pseudocode

Pseudocode is a description of a program's steps written in plain language and arranged like code, with indentation showing which steps belong inside which. It is not written in any programming language and cannot be run.

Two conventions do most of the work:

  • One instruction per line. If a line is doing two things, split it.
  • Indent what is inside something else. The two indented lines above are the ones that repeat; print total is not indented, so it happens once at the end.
Definition 1.5.5 — Pseudocode Six-line pseudocode listing with the two lines nested inside the while indented one step. A bracket draws in to enclose the indented pair, then two labels appear in turn: one naming the bracketed lines as repeating inside the while, the other naming the final unindented line as running once. set total to 0 set i to 1 while i is 5 or less add i to total add 1 to i print total inside the while - repeats outside - runs once

Definition 1.5.5 — Pseudocode: indentation shows containment, so the indented lines live inside the while and the unindented one runs once.

Example 1.5.1: Pseudocode for a decision
ask the user for their age
if age is 18 or more
    print "You may vote"
otherwise
    print "Too young to vote"

Note what this does not say: nothing about how to ask, what a variable is, or which language this will become. Those are step-2 problems. The plan is right or wrong on its own terms, and you can check it by reading it to somebody.

Handling a choice: butter or no butter

Writing algorithms takes practice, and the practice is mostly in noticing the cases you skipped. Not everyone wants butter in a jam sandwich, so the robot needs a way to add it or not depending on the answer. Three steps cover it:

No compiler is the point

Pseudocode is worth writing precisely because no machine checks it. A syntax error tells you nothing about whether your plan makes sense. Reading five lines of plain English to a classmate finds "you never told it to stop" far faster than a debugger will.

  1. Ask whether there should be butter on the bread.
  2. Either spread butter on the bread,
  3. Or do not use butter.

Those become new actions in Table 1.5.1 and new steps in the pseudocode, written with keywords such as INPUT, OUTPUT, IF, THEN, ELSE, and START:

START
INPUT "Do you want butter?" as answer
IF answer is yes THEN
    spread butter on the bread
ELSE
    do nothing
spread jam on the bread
OUTPUT the sandwich

The same instructions can then be turned into a flowchart, which Section 1.5.5 takes up.

Concepts in Practice: Pseudocode

1. What is wrong with this pseudocode?

set count to 1
while count is less than 10
    print count
  1. Nothing.
  2. count never changes, so it will repeat forever.
  3. Pseudocode cannot use while.
Solution

b. Nothing ever increases count, so the condition stays true and the plan never finishes. The plan is wrong, and you can see it is wrong without writing any code at all — which is the point of writing the plan first.

Try It Now 1.5.4

Write pseudocode for finding the largest of three numbers. Use indentation to show which steps are inside a decision.

Solution

One correct answer:

set largest to the first number
if the second number is bigger than largest
    set largest to the second number
if the third number is bigger than largest
    set largest to the third number
print largest

Others work too. Pseudocode has no single right form, only clearer and less clear ones.

1.5.5 Flowcharts

A flowchart shows the same kind of plan as a picture: boxes for steps, arrows for the order they happen in. Where pseudocode is easier to write, a flowchart is easier to see — especially when a program branches.

Three shapes carry nearly all flowcharts, and a fourth turns up often enough to be worth knowing:

Table 1.5.2 — The three core flowchart shapes, and a fourth you will meet.
ShapeMeaning
OvalStart or end
RectangleA step — do this
DiamondA decision — a question with two exits, labelled yes and no
ParallelogramInput or output — a value read in, or a result sent out. Optional: a rectangle says the same thing when the difference does not matter

Figure 1.5.7 pairs the three core symbols with the instruction each one stands for, and adds the arrow that carries the order from one step to the next.

Figure 1.5.7 — Flowchart symbols A ruled two-column table. The tinted header row reads Symbol and Instruction. Row 1 pairs a stadium (rounded rectangle) outline with Start/end. Row 2 pairs a rectangle outline with Task (e.g., spread jam). Row 3 pairs a diamond outline with Decision (e.g., do you want butter?). Row 4 pairs a down-right arrow with Direction of flow. Static figure, no motion. Symbol Instruction Start/end Task (e.g., spread jam) Decision (e.g., do you want butter?) Direction of flow

Figure 1.5.7 — The symbols used in a flowchart are associated with their instructions.

Section 1.5.4's voting decision makes a good first example. Definition 1.5.6 draws it out.

Definition 1.5.6: Flowchart

A flowchart is a diagram of a program's steps, using ovals for start and end, rectangles for actions, and diamonds for decisions, connected by arrows showing the order of execution.

Definition 1.5.6 — Flowchart Start oval, a 'get the age' process, an 'age is at least 18' decision with the no branch running right and the yes branch running down, two print processes that rejoin, and an End oval. Each shape family highlights in turn beside a label naming its role. oval — start and end rectangle — an action diamond — a decision,two ways out arrows — the orderof execution Start End get the age print "Too young" print "You may vote" age >= 18 yes no the two paths join upand the program goes on

Definition 1.5.6 — A flowchart is a diagram of a program's steps, using ovals for start and end, rectangles for actions, and diamonds for decisions, connected by arrows showing the order of execution.

The diamond in that drawing is the part worth studying. It has one way in and two ways out, and the two paths join up again afterwards. That shape is selection from Section 1.4.4, drawn.

When to use which

Neither tool is better; they are good at different things.

In practice most programmers reach for pseudocode by default and draw a flowchart when a piece of logic gets tangled enough that they cannot hold it in their head.

Concepts in Practice: Flowchart shapes

1. Which shape has two arrows leaving it?

  1. The oval
  2. The rectangle
  3. The diamond
Solution

c. The diamond is a decision, so it has a yes path and a no path. Ovals and rectangles have exactly one way onward.

Try It Now 1.5.5

Sketch a flowchart for this task: get a number; if it is even, print "even"; otherwise print "odd". Name the shape you used for each step.

Solution

Five shapes, in order:

  1. Oval — Start.
  2. Rectangle — get the number.
  3. Diamond — is the number even? Two exits, yes and no.
  4. Rectangle on each branch — print "even" on the yes path, print "odd" on the no path.
  5. Oval — End, with both branches joining back together before it.

The branches rejoining is the detail worth checking in your own sketch. A flowchart whose paths never meet again has two endings, which almost always means a step was forgotten.

1.5.6 How Algorithms Get Executed

So far every plan in this section has been sequential: one step, then the next, in written order. That is the traditional execution model, and it is the one you should assume unless told otherwise. Two other models are worth knowing about, because they change what the same set of instructions costs to run.

Parallel and concurrent execution

Parallel and concurrent execution models are used to make an algorithm finish sooner. Suppose you and a friend are buying tickets for a movie and there are three separate lines. Splitting up — you in one line, your friend in another — is a parallel model, and you are guaranteed to get the tickets sooner as long as one line moves faster than the other, which it usually does.

Running the same algorithm truly simultaneously on a computer may not be possible if the machine has only one central processing unit (CPU). In that case you can simulate parallelism: the operating system runs the two algorithms concurrently, as separate tasks sharing the one processor. That is less efficient than true parallelism, because nothing is actually happening at the same instant — the processor is just switching between tasks quickly. Chapter 4 Linguistic Realization of Algorithms: Low-Level Programming Languages goes into the difference in detail.

Recursion

Recursion is the problem-solving technique where a process calls itself to solve smaller instances of the same problem. It can be a powerful tool, because it allows an elegant solution to a complex problem by breaking it into smaller, more manageable parts. Programmers use it to write concise, efficient code across a wide range of problems.

Definition 1.5.7: Recursion

Recursion is a technique in which a function calls itself on a smaller version of the same problem, stopping at a base case — an input simple enough to answer directly without calling itself again.

Definition 1.5.7 — Recursion Four call frames step down and to the right, recursiveSum(3) through recursiveSum(0), each deferring to the one below it, with an empty returns column and the function's JavaScript on the right. The deepest frame turns amber and answers 0 directly; the answers then travel back up one level at a time as 1, 3 and 6. recursion returns the function recursiveSum(3) recursiveSum(2) recursiveSum(1) recursiveSum(0) 3 + 3 = 6 2 + 1 = 3 1 + 0 = 1 0 function recursiveSum(x) { if (x === 0) { return 0; base case } else { return x + recursiveSum(x - 1); } } base case -- answers directly, without calling itself again the chain unwinds only after the base case answers

Definition 1.5.7 — Recursion is a technique in which a function calls itself on a smaller version of the same problem, stopping at a base case — an input simple enough to answer directly without calling itself again.

The main advantage is handling a complex task with very little code. Instead of writing a long iterative loop for a repetitive task, you define a process that calls itself with modified input, which cuts down the amount of code needed.

It also has a specific way of going wrong. An improper implementation causes stack overflow errors, because too many calls pile up waiting for each other. A recursive process must have a proper base case so the recursion terminates and does not loop forever.

Here is a recursive sum. The original source writes it in C++; below it is the same algorithm in JavaScript, since that is the language this book uses. The logic is identical — recursion is a property of the algorithm, not of any one language.

Editor
runs in your browser
▶ Press Run to see the output…

What you should see:

55

Read it as two claims rather than as a loop. The first: the sum from 0 to 0 is 0. That is the base case — the simplest possible version of the problem, and it needs no further work. Written on its own, the function starts like this:

function recursiveSum(x) {
  // Base case
  if (x === 0) {
    return 0;
  }
}

The second claim: the sum from 0 to x is x plus the sum from 0 to x - 1. That is the recursive step, and it is what makes the value of x shrink on every call until the base case catches it.

Deferred, not repeated

It looks like recursion just calls the same function over and over, and that is only half true. What is really happening is a chain of deferred operations — recursiveSum(10) cannot finish until recursiveSum(9) answers, and so on down. Those pending operations are not visible in your code or your output; they are held in memory until the base case lets the chain unwind. Leave out the base case and the chain never ends.

An iterative version of the same problem — adding n numbers with a loop instead of a call chain — is shown as a flowchart in Figure 1.5.8. Comparing the two is a good way to see what recursion actually buys you: fewer moving parts in the code, at the cost of some work moving into memory where you cannot see it.

Figure 1.5.8 — Iterative-sum flowchart Start, an initialization box, and a decision diamond sit on a left spine; the diamond's Yes branch runs right into a loop box whose own exit runs up and left to rejoin the spine just above the diamond, while the No branch runs down into Print Sum and End. A marker starts at Start, reaches the diamond, rides the loop twice (a second and third arrival at the diamond), and only on the third arrival takes No down to Print Sum and End. Start End i = 0, Sum = 0Read N Sum = Sum + ii = i + 1 Print Sum If i <= N Yes No

Figure 1.5.8 — A flowchart represents an iterative solution for adding n numbers.

Try It Now 1.5.6

Run the code below. It is missing its base case. Predict what will happen before you press Run, then add the base case so it prints 55.

function recursiveSum(x) {
  return x + recursiveSum(x - 1);
}

console.log(recursiveSum(10));

Type it into the editor and run it:

Editor
runs in your browser
▶ Press Run to see the output…
Solution

Without a base case, x keeps decreasing past 0 forever and the calls pile up until the browser gives up. You will see something like:

RangeError: Maximum call stack size exceeded

That message is the stack overflow described above, and it is worth causing once on purpose so you recognize it later. The fix is the missing stopping condition:

Editor
runs in your browser
▶ Press Run to see the output…

Output:

55

1.5.7 Where JavaScript Runs

A plan eventually has to become a running program, which needs somewhere to run. For JavaScript that is a browser — every browser has a JavaScript engine built in, which is why this book needs no installation.

There are two places you will meet.

The browser console

Every browser has a developer console: a place to type JavaScript and see it run immediately. Open it with F12, then choose the Console tab.

Type this and press Enter:

Editor
runs in your browser
▶ Press Run to see the output…

What you should see:

Hello from the console

The console is for quick questions — checking what a value is, trying a line you are unsure about. Everything you type is forgotten when you reload the page, which is exactly what makes it comfortable to experiment in.

Definition 1.5.8: Developer Console

The developer console is a panel built into the browser where JavaScript can be typed and run immediately, and where error messages and console.log output appear. It is opened with F12 in most browsers.

Definition 1.5.8 — Developer console A browser-console panel: header strip with an F12 badge. A prompt marker sits at the left, then console.log("Hello from the console"); types out one character at a time with the cursor advancing as it goes. On Enter the answer "Hello from the console" prints at once - a console echoes its output, it does not type it - and the cursor rests immediately after it. Both lines and the cursor remain on screen at the end. F12 > console.log("Hello from the console"); Hello from the console

Definition 1.5.8 — The developer console: type a line, press Enter, and the answer prints in the same panel.

A code editor

For anything you want to keep, you use a code editor — a text editor built for code, which colours your syntax, indents for you, and flags some mistakes before you run anything. Visual Studio Code is the common choice and is free.

The difference is permanence. The console is a scratchpad; the editor holds the program. Most work is done in the editor with the console open beside it to see the output.

The boxes on this page are both at once

The runnable boxes in this book are a third option, and a deliberate convenience — they are editors and consoles at the same time, so you can change an example and run it without leaving the page. Use them freely. Everything in them is ordinary JavaScript and behaves the same way anywhere else.

Try It Now 1.5.7

Open your browser's console with F12 and type 2 + 2 3, then press Enter. Then type it again as (2 + 2) 3. What are the two answers, and what does the difference tell you?

Solution

The first prints 8, the second prints 12.

Multiplication happens before addition unless parentheses say otherwise, so 2 + 2 * 3 is 2 + 6. The useful part is not the arithmetic — it is that you settled the question in about four seconds without writing a file, saving it, or reloading anything. That is what the console is for.

1.5.8 Reading an Error Message

When a program goes wrong, the browser tells you what happened. Beginners often skim past the message because it looks technical; it is usually the most useful sentence available.

An error message has three parts worth reading:

Uncaught ReferenceError: totl is not defined
    at line 3
  1. The kindReferenceError. This one means a name was used that does not exist.
  2. The detailtotl is not defined. It names the exact thing it could not find.
  3. The place — the line number.

That message is telling you there is a typo, and telling you what the typo says. The fix is to compare totl with the name you meant.

Editor
runs in your browser
▶ Press Run to see the output…

What you should see:

Total is 10

Change total to totl on the second line and run it again. You will get exactly the error above, and you will have caused it on purpose — which is a genuinely good way to learn to read messages, because you already know what the answer is.

Describing the problem often solves it

"It doesn't work" is not a description, and it is the hardest thing to get help with. "It says ReferenceError: totl is not defined on line 3" describes a problem, and often answers it while you are typing it out. Reading the message is the first debugging step and it is free.

Concepts in Practice: Errors

1. What does ReferenceError: score is not defined tell you?

  1. score holds the wrong value.
  2. Nothing named score exists — usually a typo or a missing declaration.
  3. score must be declared with const.
Solution

b. The name was used but never created. Check the spelling first, then check that the line declaring it actually runs before this one.

Try It Now 1.5.8

Run the code below, read the error it produces, and say which of the three parts of the message told you where to look. Then fix it.

let greeting = "hello";
console.log(gretting);

Type it into the editor and run it:

Editor
runs in your browser
▶ Press Run to see the output…
Solution

The error is:

Uncaught ReferenceError: gretting is not defined

The detailgretting is not defined — is the part that solves it: the name it could not find is spelled with the letters transposed. The place narrows it to the line; the kind tells you it is a name problem rather than a value problem.

The fix:

Editor
runs in your browser
▶ Press Run to see the output…

Output:

hello

1.5.9 Testing and Debugging

Testing and debugging are the techniques used to find flaws in algorithms and defects in code so they can be corrected.

Test cases provide specific input data to check whether a program functions correctly and meets its requirements. You have to identify the test cases before you can run any tests. When a test fails, debugging finds the source of the problem and fixes it. In other words, debugging is about locating and fixing defects — bugs — in algorithms and processes, so they behave as expected.

Everyone makes mistakes in programming; they are part of learning. What matters is identifying the mistake and working out how to get past it. There is a good case to be made that the deepest learning happens exactly when something goes wrong.

Definition 1.5.9: Debugging

Debugging is the process of finding and fixing the cause of incorrect behaviour in a program. Its basic technique is to compare what you believe the program is doing with what it is actually doing.

Definition 1.5.9 — Debugging Two columns, You believed and What happened, over three rows - price, quantity, total. Price and quantity each get a check mark and dim to ruled-out; total gets an X, stays at full opacity, and is framed by a highlight box labeled the bug. Debugging: Belief vs. Reality You believed What happened price 20 20 quantity 3 3 total 60 23 the bug

Definition 1.5.9 — Debugging: compare what you believe against what actually happens, until one line is left.

Testing the sandwich

In the jam sandwich algorithm, testing works by taking turns: one person plays the programmer who wrote the instructions, the other plays the robot. The programmer reads out each instruction — from the pseudocode or from the flowchart — and the robot follows it exactly. Each instruction is a test case, and the test succeeds if the robot can carry it out precisely and successfully. Otherwise you debug the instruction: find the source of the problem and correct it. Table 1.5.3 is a sheet for recording what went wrong and what needs to change.

Table 1.5.3 — A sheet for recording test problems and the improvements they call for.
Test caseInputProblemExpected outcomeObserved outcomeImprovementResponsible user
1.
2.

Finding out what a program is doing

Most bugs are not crashes. The program runs happily and produces the wrong answer, and there is no message at all — nothing is broken as far as JavaScript is concerned. For those, the basic tool is console.log.

The technique is to print what you believe is true and check whether it is:

Editor
runs in your browser
▶ Press Run to see the output…

What you should see:

price is 20
quantity is 3
total is 23

The program did not fail. It printed 23, and a total of 23 for three items at 20 each is wrong — the + should be *. Nothing but printing the values would have shown you that.

Two habits make this much more effective:

Browsers offer more powerful tools — a debugger that pauses a program mid-run and lets you inspect it line by line. console.log remains the one every programmer uses most, because it takes three seconds and answers the question most of the time.

Technology in Everyday Life: DNA sequencing

Computational thinking matters in every industry, and DNA sequencing is a good illustration because the data is far too large to reason about directly. The work starts by breaking a long DNA sequence into smaller pieces. Then patterns within those pieces are identified — sequences that might indicate a particular gene. The focus stays on the most relevant regions, discarding data that is not needed, so attention lands on potentially significant genetic material. Finally, refined algorithms process and reconstruct the original sequence to identify genetic variations.

Every part of computational thinking shows up in that process:

Concepts in Practice: Computational thinking in the real world

Here are four real-life scenarios and a suggested solution approach for each. Try to name the technique at work before opening the solution.

Organizing a city's recycling program to maximize efficiency. How do you ensure the most effective collection routes and times?

Solution

Use a route optimization algorithm to analyze and plan the most efficient paths for collection trucks, taking factors like distance and traffic patterns into account.

Planning the layout of a community garden to optimize space and sunlight exposure for different plant types. How do you decide where to plant each type of vegetable or flower?

Solution

Employ a simulation algorithm that models sunlight patterns, plant growth rates, and space requirements, and use it to design a layout that maximizes space and plant health.

Creating a schedule for a multistage music festival to minimize overlaps and keep audiences moving smoothly. How do you schedule the performances across different stages?

Solution

Implement a scheduling algorithm that weighs audience preferences, artist availability, and stage logistics to build a timetable that maximizes attendee satisfaction and minimizes conflicts.

Determining the most efficient way to allocate computer resources in a cloud environment with varying user demand. How do you manage the computational load?

Solution

Use load balancing algorithms to distribute tasks across servers dynamically, keeping resource use optimal and system performance steady.

Try It Now 1.5.9

The code below should print the area of a rectangle 4 by 5, which is 20. Add console.log lines to find out what it is actually doing, then fix it.

let width = 4;
let height = 5;
let area = width + height;

console.log("The area is " + area);

Type it into the editor and run it:

Editor
runs in your browser
▶ Press Run to see the output…
Solution

Print the pieces first:

Editor
runs in your browser
▶ Press Run to see the output…

Output:

width is 4
height is 5
area is 9

width and height are both right, so the mistake is in the line that combines them — + should be *:

Editor
runs in your browser
▶ Press Run to see the output…

Output:

The area is 20

Note the method: the two prints that showed correct values were not wasted. Ruling out the inputs is what left only one place for the mistake to be.

Problem Set 1.5

1.5.1 Define computational thinking in one or two sentences, without using the word "computer."

Solution

Step 1 — Strip the definition down to its non-technical core: The section's definition ties computational thinking to breaking a hard problem into pieces and finding a systematic way to handle each piece. None of that requires naming the machine that eventually runs the plan.

Answer: Computational thinking is a problem-solving process that breaks a complex, tangled problem into smaller, manageable parts and works out a systematic approach for solving each one.

1.5.2 Name the four techniques of computational thinking and give a one-line description of each.

Solution

Step 1 — List the four techniques in the order the section gives them: Section 1.5.1 names decomposition, logical thinking and pattern recognition, abstraction, and algorithms as the four techniques, sometimes called the cornerstones of computational thinking.

Step 2 — Give each one its one-line description:

  • Decomposition — breaking a complex problem into smaller, more manageable subproblems.
  • Logical thinking and pattern recognition — identifying similarities and recurring structures within and among problems.
  • Abstraction — focusing on the information that matters and ignoring irrelevant detail.
  • Algorithms — writing a detailed, step-by-step set of instructions for solving a problem.

Answer: Decomposition, logical thinking and pattern recognition, abstraction, and algorithms — described above.

1.5.3 Name the three As and say what each one covers.

Solution

Step 1 — Recall the shorter framing from Figure 1.5.2: The section gives the three As as a compressed version of the same idea as the four techniques, one A per stage of solving a problem.

Step 2 — State what each A covers:

  • Abstraction — formulating the problem: figuring out what actually matters about it.
  • Automation — expressing the solution: turning the plan into something that can run repeatedly.
  • Analysis — executing and evaluating the solution: running it and checking whether it worked.

Answer: Abstraction (formulate the problem), Automation (express the solution), Analysis (execute and evaluate the solution).

1.5.4 Decompose the problem "make a cup of tea" into at least six tasks, then group those tasks into categories.

Solution

Step 1 — List out every task making tea actually requires: Following the same approach as the jam sandwich and Lucía examples, break "make a cup of tea" into individual, concrete tasks: fill the kettle, boil the water, get a mug, put a tea bag in the mug, pour the hot water over the bag, wait for it to steep, remove the bag, add milk or sugar if wanted.

Step 2 — Group the tasks into categories: One reasonable grouping:

  • Getting ready — fill the kettle, get a mug, put the tea bag in the mug.
  • Heating — boil the water, pour it over the bag.
  • Finishing — wait for it to steep, remove the bag, add milk or sugar.

Answer: At least six tasks (fill the kettle, boil the water, get a mug, add the tea bag, pour the water, steep, remove the bag, add extras), grouped into categories such as getting ready, heating, and finishing. Any grouping that keeps similar tasks together is acceptable — the specific labels aren't the point.

1.5.5 Give one example of inductive reasoning and one of deductive reasoning from outside programming.

Solution

Step 1 — Recall what separates the two kinds of reasoning: Inductive reasoning goes from specific observations to a general principle. Deductive reasoning starts from premises and forces a conclusion that must be true if the premises are true.

Step 2 — Give an inductive example outside programming: Every cat you have ever met has retracted claws you only see when it swipes. After enough cats, you form the general rule "cats have retractable claws" — a pattern built up from repeated specific observations.

Step 3 — Give a deductive example outside programming: All the fruit in this bowl is apples. You just took a piece of fruit from this bowl. Therefore, what you're holding is an apple — the conclusion is forced by the premises, not just likely.

Answer: Inductive — generalizing "cats have retractable claws" from many individual cats. Deductive — concluding "this is an apple" from "all the fruit in the bowl is apples" and "I took a piece of fruit from the bowl."

1.5.6 You are writing a program that recommends a movie for your friend Aiko. Name two details you would abstract away and say why neither one changes what she ends up watching.

Solution

Step 1 — Apply the abstraction test from Try It Now 1.5.3: The test is whether dropping a detail changes the action the reader has to take. For a movie recommendation, the action that matters is which movie Aiko ends up watching, so keep only details that could change that.

Step 2 — Identify two details safe to abstract away: The exact time of day she'll watch it and which streaming app happens to be open on her screen. Neither one affects which movie the program picks — the recommendation only needs to depend on things like her genre preferences and watch history, not on when or where she presses play.

Answer: Abstract away the time of day and the specific app or device she watches on. Neither changes which movie gets recommended, so keeping them would only add irrelevant detail to the program.

1.5.7 Explain in one or two sentences why working out a plan before typing is faster than typing your way to an answer.

Solution

Step 1 — Recall the two separate difficulties a program presents: Section 1.5.1 splits a programming problem into working out what the steps are (thinking) and writing those steps in JavaScript (syntax). Typing immediately forces you to solve both at once.

Answer: Planning first is faster because it separates the two problems — figuring out the steps and writing correct syntax — so that when you get stuck you know which one you're stuck on, instead of getting confused by both at the same time.

1.5.8 What is wrong with this pseudocode, and how would you fix it?

set count to 1
while count is less than 10
    print count
Solution

Step 1 — Trace what the loop actually does: count starts at 1 and the loop keeps running as long as count is less than 10, printing count each time. Nothing inside the loop ever changes the value of count.

Step 2 — Identify the consequence: Since count stays at 1 forever, the condition "count is less than 10" never becomes false, so the loop never stops — it prints 1 forever.

Step 3 — Fix it: Add a line inside the loop that increases count on each pass, so the condition eventually becomes false:

set count to 1
while count is less than 10
    print count
    add 1 to count

Answer: The pseudocode never changes count, so the loop never ends. The fix is to add add 1 to count inside the loop.

1.5.9 Write pseudocode for finding the largest of three numbers.

Solution

Step 1 — Decide what "largest" needs to track: We need one variable that holds the biggest value seen so far, updated as we look at each number.

Step 2 — Start it off with the first number: Since we have nothing to compare yet, the first number is the best guess for the largest until proven otherwise.

Step 3 — Compare the other two against it: Each remaining number either beats the current largest or it doesn't — a decision, so each comparison gets its own if.

set largest to the first number
if the second number is bigger than largest
    set largest to the second number
if the third number is bigger than largest
    set largest to the third number
print largest

Answer: The pseudocode above — keep a running largest, update it once per remaining number, print it at the end.

1.5.10 Write pseudocode for a program that asks for a password and keeps asking until it is correct.

Solution

Step 1 — Identify what has to repeat: The program has to keep asking as long as the password is wrong, which is a loop condition rather than a one-time check.

Step 2 — Ask once before the loop starts: The loop needs something to test the first time through, so we get one password before checking anything.

Step 3 — Loop while it's wrong, asking again each time: Inside the loop, tell the user it was wrong and ask again — the loop only ends once the input matches.

set correct to "sunshine"
ask the user for a password
while the password does not equal correct
    print "Wrong password, try again"
    ask the user for a password
print "Access granted"

Answer: Ask once before the loop, then loop on "while the password does not equal correct," re-asking inside the loop until it matches.

1.5.11 Name the three flowchart shapes and what each means.

Solution

Step 1 — Name the three shapes: A flowchart is built from an oval, a rectangle, and a diamond, and each one means something different in the diagram.

Step 2 — State what each one stands for: Oval marks the start or the end of the program. Rectangle marks a step — an action to carry out. Diamond marks a decision, a question with two exits labelled yes and no.

Answer: Oval = start or end, rectangle = a step, diamond = a decision with a yes exit and a no exit.

1.5.12 Which flowchart shape has two arrows leaving it, and why?

Solution

Step 1 — Check which shape branches: Ovals and rectangles each have exactly one arrow leaving them, since a start, an end, and a plain step only ever lead to one next thing.

Step 2 — Explain why the diamond is different: A diamond represents a decision, and a decision asks a yes-or-no question. Each answer sends the flow down a different path, so the diamond needs two arrows leaving it — one labelled yes, one labelled no.

Answer: The diamond. It's a decision, and a decision has two possible answers, so it needs two exits — yes and no.

1.5.13 Draw a flowchart for: get a number; if it is even print "even", otherwise print "odd".

Solution

Step 1 — Lay out the shapes in order: The task has one input, one decision, and one output that differs depending on the decision, so we need a Start, a step to get the number, a diamond to test it, two possible print steps, and an End.

Step 2 — List them:

  1. Oval — Start.
  2. Rectangle — get the number.
  3. Diamond — is the number even? Two exits, yes and no.
  4. Rectangle on the yes path — print "even."
  5. Rectangle on the no path — print "odd."
  6. Oval — End, with the two branches joining back into one path just before it.

Answer: Six shapes: Oval (Start) → Rectangle (get the number) → Diamond (is it even?) → Rectangle "even" or Rectangle "odd" depending on the answer → the two paths rejoin → Oval (End).

1.5.14 Give one advantage of pseudocode over a flowchart, and one advantage of a flowchart over pseudocode.

Solution

Step 1 — Pick pseudocode's advantage: Pseudocode is faster to write and edit than a diagram, and it doesn't get physically enormous the way a flowchart does once a program has more than a handful of steps.

Step 2 — Pick the flowchart's advantage: A flowchart makes branching and looping visible at a glance, because the shape of the diagram shows the decision points directly instead of requiring the reader to trace indentation. That also makes it the better of the two for explaining a program to someone else.

Answer: Pseudocode wins on speed and staying manageable for long programs; a flowchart wins on making branches and loops visually obvious and on explaining the logic to another person.

1.5.15 Explain the difference between parallel execution and concurrent execution on a single-CPU machine.

Solution

Step 1 — Define parallel execution: Parallel execution runs separate tasks at the same literal instant, on separate processors — like you and a friend each standing in a different ticket line at the same time.

Step 2 — Define concurrent execution on one CPU: With only one CPU, true simultaneity isn't possible. The operating system instead switches between tasks quickly, giving each one a slice of time on the single processor. Nothing is actually happening at the same instant — it only looks that way because the switching is fast.

Step 3 — State the practical difference: Parallel execution is genuinely faster because the work is split across hardware; concurrent execution on one CPU is a simulation of that speedup, and it's less efficient because the processor is still doing one thing at a time underneath.

Answer: Parallel execution runs tasks simultaneously on separate processors; concurrent execution on a single-CPU machine interleaves those same tasks on one processor, so they only appear to run at once.

1.5.16 What is a base case, and what happens to a recursive function that does not have one?

Solution

Step 1 — Define base case: A base case is the input simple enough for a recursive function to answer directly, with no further call to itself — it's what stops the chain of calls.

Step 2 — Explain what happens without one: Without a base case, every call makes another call, and the input never reaches a point where the function can just return an answer. The calls pile up waiting on each other until the browser runs out of room for them, producing a RangeError: Maximum call stack size exceeded — a stack overflow.

Answer: A base case is the simplest input a recursive function can answer without calling itself again. Leave it out and the function recurses forever in principle, which in practice crashes with a stack overflow.

1.5.17 Rewrite recursiveSum as a loop instead of a recursive function, and say which version you find easier to read.

Editor
runs in your browser
▶ Press Run to see the output…
Solution

Step 1 — Recall what the recursive version is doing: recursiveSum(x) returns 0 the moment x hits 0 (the base case), and otherwise returns x plus the sum of everything below it — a chain of calls that cannot finish until the base case answers.

Step 2 — Replace the chain with a running total: A loop does not need to defer anything. It can just add each number from 1 up to x into a variable as it goes.

Editor
runs in your browser
▶ Press Run to see the output…

What you should see:

55

Step 3 — Compare the two: The loop is easier to read. Everything the function is tracking — total, i — sits in plain view on one line each, and you can watch total grow step by step. The recursive version is shorter, but as the Insight Note in Section 1.5.6 points out, its ten pending additions are held in memory where you can't see them; you have to trust the base case to unwind them correctly instead of watching it happen.

Answer: recursiveSumLoop above reproduces recursiveSum, and the loop is the easier one to read — nothing is hidden in a call chain.

1.5.18 What is the difference between the browser console and a code editor, and when would you use each?

Solution

Step 1 — State what each one is: The browser console is a panel, opened with F12, where you type JavaScript and it runs immediately. A code editor is a text editor built for code — it colours syntax, indents for you, and flags some mistakes before you run anything.

Step 2 — State what separates them: Permanence. Everything typed into the console is forgotten the moment the page reloads. A file written in an editor stays saved.

Step 3 — Say when to use each: Reach for the console when you have a quick question — checking what a value is, or trying one line you're unsure about. Use the editor for any program you intend to keep, and run it with the console open beside it to see the output.

Answer: The console is a scratchpad for immediate, throwaway checks; the editor is where a real program is written and kept. Most work happens in the editor, with the console open to watch the results.

1.5.19 Name the three parts of an error message and say what each tells you.

Solution

Step 1 — Name the three parts: Using ReferenceError: totl is not defined at line 3 as the example, the parts are the kind, the detail, and the place.

Step 2 — Say what each tells you: The kind (ReferenceError) names the category of problem — here, a name was used that doesn't exist. The detail (totl is not defined) names the exact thing JavaScript couldn't find. The place (the line number) tells you where in your code to start looking.

Answer: The three parts are the kind, the detail, and the place — what went wrong, exactly what it was about, and where it happened.

1.5.20 What does ReferenceError: score is not defined tell you, and what should you check first?

Solution

Step 1 — Read what the error says: ReferenceError means a name was used that does not exist anywhere in the program. The detail, score is not defined, tells you the missing name is score.

Step 2 — Say what it means in practice: Nothing named score was ever created — either the spelling doesn't match, or the line that declares it never ran before this one.

Step 3 — Say what to check first: Compare the spelling of score everywhere it appears. Only after ruling out a typo should you check whether the declaration runs before the line that uses it.

Answer: It tells you score was never created. Check the spelling first, then check that the declaration actually runs before this line.

1.5.21 Why is console.log("total is " + total) more useful than console.log(total)?

Solution

Step 1 — Compare what each line prints: console.log(total) prints a bare number, with nothing attached to say what it is or which line produced it. console.log("total is " + total) prints the same number with a label in front of it.

Step 2 — Say why the label matters: A debugging session usually prints several values in a row. A screen full of bare numbers gives you no way to tell which one is which; a labelled line tells you immediately.

Answer: The labelled version is more useful because it says what the number means, not just what it is — exactly the habit Section 1.5.9 calls "label every print."

1.5.22 The code below should print 20 but prints 9. Explain how you would find the mistake, then state the fix.

let width = 4;
let height = 5;
let area = width + height;

console.log("The area is " + area);
Editor
runs in your browser
▶ Press Run to see the output…
Solution

Step 1 — Print the inputs first: Add console.log lines for width and height before trusting the line that combines them.

Editor
runs in your browser
▶ Press Run to see the output…

What you should see:

width is 4
height is 5
area is 9

Step 2 — Rule out the inputs: width is 4 and height is 5, both correct. That leaves the line that combines them as the only place the mistake can be — area = width + height adds them (4 + 5 = 9) instead of multiplying them.

Step 3 — State the fix: Change + to *.

Editor
runs in your browser
▶ Press Run to see the output…

Output:

The area is 20

Answer: Print width, height, and area separately to see which one is wrong; the inputs are fine, so the bug is width + height where it should be width * height.

1.5.23 Explain the difference between testing and debugging in one sentence each.

Solution

Step 1 — Define testing: Testing runs specific test cases against a program to check whether it produces the outcome it's supposed to.

Step 2 — Define debugging: Debugging is what happens after a test fails — finding and fixing the cause of the incorrect behaviour by comparing what you believe the program is doing with what it's actually doing.

Answer: Testing checks whether something is wrong; debugging finds and fixes why it's wrong.

Key Terms

Computational thinking -- A problem-solving process that breaks a complex problem into smaller parts and devises systematic approaches to solve them.

Complex problem -- A problem that is difficult because it involves many interrelated parts, and rarely has a simple solution.

Decomposition -- Breaking a complex problem into smaller, self-contained parts that can be solved on their own.

Pattern recognition -- Grouping the parts a decomposition turned up into categories, and spotting recurring structures across problems.

Inductive reasoning -- Going from specific examples to a general principle.

Deductive reasoning -- Drawing a conclusion that must be true if the premises are true.

Abstraction -- A simplified representation of a complex system that keeps only the details relevant to the problem.

Data structure -- A container that organizes and stores data efficiently, defining how it is arranged and accessed.

Algorithm -- A finite, ordered sequence of unambiguous instructions that solves a problem or completes a task.

Pseudocode -- A program's steps written in plain language and arranged like code, with indentation showing nesting. Not runnable in any language.

Flowchart -- A diagram of a program's steps: ovals for start and end, rectangles for actions, diamonds for decisions, arrows for order.

Decision (diamond) -- The flowchart shape with one way in and two ways out, labelled yes and no. Selection, drawn.

Parallel execution -- Running parts of an algorithm at the same instant on separate processors.

Concurrent execution -- Interleaving separate tasks on one processor so they progress together without running at the same instant.

Recursion -- A technique in which a function calls itself on a smaller version of the same problem, stopping at a base case.

Base case -- The input simple enough for a recursive function to answer directly, which ends the chain of calls.

Developer console -- The panel built into a browser for running JavaScript immediately and reading output and errors. Opened with F12.

Code editor -- A text editor built for code, with syntax colouring and indentation help; where programs you intend to keep are written.

Error message -- The browser's report of a failure, in three parts: the kind of error, the specific detail, and the line.

ReferenceError -- The error raised when a name is used that does not exist; usually a typo or a missing declaration.

Test case -- Specific input data used to check whether a program meets its requirements.

Debugging -- Finding and fixing the cause of incorrect behaviour by comparing what you believe a program does with what it actually does.

console.log -- The basic debugging tool: print a labelled value to check whether it is what you expected.