Leer en español · Lire en français · Auf Deutsch lesen
Section 0.1 of 0.8 · Beginner · 15 min read
Programs, code and how a computer runs them
Video lesson
4 min · narrated, with captionsListen only (audio)
Read the transcript
Welcome to Be Software Architecture Wise. In this first lesson we start at the very beginning: what software is, and what really happens when a computer runs a program. You don't need any technical background. By the end, you'll know the handful of ideas that every later chapter builds on.
Let's begin with two words you'll hear all the time. Hardware is the physical machine: the processor, the memory, the storage and the network card. Software is the set of instructions and data that tell that hardware what to do. A useful picture is a restaurant kitchen. The chef is the processor, and the software is the recipes the chef follows.
A program is a list of precise instructions, and the computer follows them exactly, word for word. It never guesses what you meant. That's why mistakes in the instructions turn into bugs. The computer isn't being difficult; it's doing precisely what it was told. Programmers write those instructions as source code, in languages such as Python, Java or Go, which people can read and change.
But the processor can't read Python or Java. Its own language is machine code: very simple instructions, specific to one type of processor. So source code has to be translated. There are two main routes. In the first route, a compiler translates the whole program into machine code before it is released. Languages such as C, Rust and Go work this way. In the second route, the code is compiled into an in-between form called bytecode, and a program called a virtual machine runs it, translating busy parts into machine code as it goes. Java, Python and C sharp work like this.
Between your programs and the hardware sits the operating system, such as Windows, macOS, Linux or Android. Applications, like a web browser or a food delivery app, don't touch the hardware directly. They ask the operating system for what they need, through what are called system calls. The operating system shares the processor, the memory, the files and the network between all the programs that are running, and it keeps them from interfering with each other. In our kitchen, it's the manager deciding which recipe gets the chef's attention.
When a program runs, the operating system creates a process for it, with its own private memory. Inside one process there can be several threads, each doing a piece of work at the same time. Threads share the process's memory. That's useful, but if two threads change the same data at once, you get some of the hardest bugs to find. And notice the difference between memory and disk. Memory, or RAM, is fast but it's wiped when the program stops or the machine restarts. Anything that must not be lost has to be saved to durable storage, usually a database.
Let's put it together with a tiny program that works out the price of an order. When you start it, the operating system creates a process and starts the Python interpreter inside it. The interpreter reads the file and turns it into bytecode. If there's a typing mistake, such as a missing bracket, it's reported now, before anything runs. The program asks you how many items you want, and waits. While it waits, it uses no processor time at all. Then the calculation: three items at twelve pounds fifty is thirty seven pounds fifty, and adding twenty per cent VAT gives forty five pounds. Finally the result is printed and the process ends. Its memory is handed back, and because nothing was saved to disk, the answer is gone.
Why does this matter for software architecture? Because every system you'll design, however large, is made of programs running as processes on shared hardware. Decisions about what lives in memory and what must be stored durably come up again and again, in every chapter. And because many things run at once and machines restart, architects have to plan for concurrency and for failure from the start.
That's the foundation. The full section has the complete code example and more on the operating system and bugs. Next, in Section zero point two, we look at how programs organise data. See you there.
What you need to know
- Hardware is the physical machine (CPU, RAM, storage, network card); software is the set of instructions and data that tell it what to do.
- A program is a list of precise instructions; the computer does exactly what it says, so mistakes in the instructions become bugs.
- Programmers write source code in high-level languages, which compilers, interpreters or bytecode virtual machines turn into machine code the CPU can run.
- The operating system shares the CPU, memory, files and network between programs and enforces security; programs ask it for services through system calls.
- A running program is a process with its own memory; threads inside a process share that memory, which is useful but can cause concurrency bugs.
- RAM is fast but loses its contents on restart; anything that must not be lost has to be written to durable storage, usually a database.
Hardware and software
A computer is a machine that follows instructions very quickly. The physical parts you could touch are the hardware; the instructions that tell those parts what to do are the software. A laptop, a phone, the server that runs your bank's website and the chip inside a smart thermostat are all computers built from the same four kinds of part.
The CPU (central processing unit, or processor) carries out instructions. A modern processor has several cores, each able to work through its own stream of instructions, and each core runs billions of simple steps per second. RAM (random-access memory, usually just called memory) holds the instructions and data the CPU is working on right now. It is fast, but it is volatile: switch the power off and its contents vanish. Storage (a solid-state drive or a spinning hard disk) keeps files when the power is off, but is much slower to reach. A network card (wired or Wi-Fi) sends and receives data to and from other computers.
Think of a restaurant kitchen. The chef is the CPU: the only one who actually cooks. The worktop is RAM: everything the chef is using at this moment is laid out there, within arm's reach, but it is cleared at the end of the night. The pantry and freezer are storage: large, permanent and a walk away. The hatch to the dining room is the network card, through which orders arrive and dishes leave. The recipes are the software.
The four parts every computer has
| Part | What it does | Kitchen analogy | What happens if it is the bottleneck |
|---|---|---|---|
| CPU (processor) | Executes instructions: arithmetic, comparisons, moving data | The chef | Work queues up; pages and reports become slow even though there is spare memory |
| RAM (memory) | Holds running programs and the data they are using | The worktop | The system starts shuffling data to disk and slows sharply, or programs are stopped |
| Storage (SSD or disk) | Keeps files and databases permanently | The pantry and freezer | Saving and loading data becomes slow; a full disk can stop a database |
| Network card | Sends and receives data to other machines | The hatch to the dining room | Requests wait to be sent or received; users see delays |
What a program is
A program is a list of precise instructions that a computer follows to do a job: calculate a bill, display a web page, send a payment. The word that matters is precise. A human cook can cope with "add salt to taste"; a computer cannot. It does exactly what it is told, in exactly the order it is told, and nothing more. If an instruction says to divide by a number that turns out to be zero, the computer does not shrug and carry on: the program stops with an error, unless the programmer wrote further instructions saying what to do in that case.
At the lowest level the CPU repeats one cycle for as long as it is switched on: fetch the next instruction from memory, decode what it means, execute it, and move on to the next one. Each instruction is tiny: add two numbers, compare two values, copy a value from one place in memory to another, jump to a different instruction if a comparison was true. Everything a computer does, from playing a film to training an AI model, is built from billions of these small steps.
Software is the broader term: programs together with the data, configuration and documentation they need. An application (or app) is software that does something useful for a person, such as a banking app. System software, such as the operating system described below, exists mainly to run other software.
Layers: applications, the operating system and the hardware
Source code and programming languages
CPUs understand only machine code: instructions encoded as numbers, specific to a family of processors. Intel and AMD chips in most laptops and servers use one family (x86-64); the chips in phones, recent Macs and many cloud servers use another (Arm). Machine code that runs on one does not run on the other. Writing machine code by hand is slow, error-prone and almost never done today.
Instead, people write source code in a programming language: a precisely defined notation that reads partly like English and partly like mathematics. Python, JavaScript, Java, C#, Go, Rust and C are widely used examples. These are called high-level languages because they let a programmer say "add up the prices in this basket" in one or two lines instead of dozens of machine instructions, and because they hide details of the particular CPU. Source code is plain text, saved in files, and it is what programmers read, write, review and store in version control (a system that keeps a history of every change, explained in the last section of this chapter).
Languages differ in what they make easy. Python is valued for readability and is dominant in data science and AI work; JavaScript is the language every web browser runs; Java and C# are common in large business systems; Go is popular for network services; C and Rust are used where every microsecond and byte matters, such as operating systems and databases. No language is best at everything, and choosing one is partly a technical decision and partly a question of which skills a team has.
From source code to a running program: compilers, interpreters and virtual machines
Source code has to be turned into something the CPU can execute. There are three broad approaches, and real languages often mix them.
A compiler is a program that translates the whole of a program's source code into another form before it runs. Languages such as C, C++, Rust and Go are usually compiled ahead of time into machine code for a particular CPU and operating system. The result is an executable file that starts quickly and runs fast, but you must compile a separate version for each kind of machine you want to support, and the compiler rejects the program if it finds certain kinds of mistake.
An interpreter is a program that reads source code (or something close to it) and carries out its instructions as it goes, one step at a time. Interpretation makes it easy to try things out and to run the same program on any machine that has the interpreter, at the cost of speed.
Many popular languages sit in between. Java source code is compiled by a tool called javac into bytecode: compact instructions for an imaginary, idealised computer rather than for a real CPU. The Java Virtual Machine (JVM) then runs that bytecode on whatever real machine it is installed on, which is how one Java program runs on Windows, macOS and Linux unchanged. The JVM watches which parts of the program run most often and compiles those parts into machine code while the program is running, a technique called just-in-time (JIT) compilation. C# on .NET works in a similar way. The standard Python implementation, CPython, also compiles your .py files to bytecode (caching the bytecode of imported modules in .pyc files) and then runs that bytecode in its own interpreter loop.
Two routes from source code to the CPU
Comparing the approaches
| Approach | Typical languages | Strengths | Costs |
|---|---|---|---|
| Ahead-of-time compilation to machine code | C, C++, Rust, Go | Fast start-up and execution; one self-contained file to deploy | Build a separate version per CPU and operating system; slower edit-and-try cycle |
| Bytecode plus a virtual machine with JIT | Java, Kotlin, C# | One build runs on many platforms; JIT can make long-running services very fast | The runtime must be installed; slower start-up and extra memory while the JIT warms up |
| Bytecode plus an interpreter | Python (CPython), Ruby | Quick to write and try; very portable | Usually slower for heavy computation; speed-critical parts are often written in C and called from Python |
| JIT-compiled in the browser or server | JavaScript (V8 in Chrome and Node.js) | Runs everywhere a browser runs; fast enough for most web work | Performance can be less predictable than compiled code |
The operating system's job
When you switch on a computer, the first substantial program to start is the operating system (OS): Windows, macOS, Linux, Android or iOS. Most servers on the internet run Linux. The operating system has four main jobs.
First, it shares the CPU. A laptop may have a few dozen programs running but only a handful of cores, so the OS switches each core between programs many times a second, fast enough that they all appear to run at once. Second, it manages memory, giving each program its own region and stopping one program from reading or overwriting another's. Third, it provides files and devices: programs ask the OS to open a file, draw on the screen or send data over the network, and the OS, through small programs called drivers, deals with the particular hardware. Fourth, it enforces security: users, permissions and the rule that an app may not read your photos unless you allow it.
Programs make these requests through system calls, a fixed set of services the OS offers. This is the first example in this course of an important architectural idea: a layer that hides messy details behind a stable set of requests. The person writing a food-delivery app does not need to know which brand of network chip the phone has.
Processes, threads, memory and disk
A program file sitting on disk is just stored instructions. When you start it, the OS creates a process: a running instance of the program with its own private memory, its own identity and its own share of CPU time. Open two browser windows and you may have several processes; start a web server and it may run as one process or many.
Inside a process there can be several threads. A thread is one sequence of instructions being carried out. A process with several threads can do several things at once, for example handle two customers' requests at the same time, and all its threads share the process's memory. That sharing is convenient but risky: if two threads change the same piece of data at the same moment without coordination, the result can be wrong in ways that appear only occasionally. These concurrency bugs are among the hardest to find, which is one reason many systems prefer separate processes, or designs where each request is handled independently.
The difference between memory and disk explains a great deal of software design. Data in RAM is quick to reach, roughly a tenth of a microsecond, but it disappears when the process ends, crashes or the machine restarts. Data on disk survives, but reaching it takes far longer: tens of microseconds or more on a solid-state drive, and several milliseconds on a spinning disk. So any information that must not be lost, such as an order or a payment, has to be written to durable storage, usually through a database, before the system tells the customer "done".
A process, its threads, its memory and the disk
PRICE_PER_ITEM = 12.50 # pounds
VAT_RATE = 0.20 # 20 per cent
def total_with_vat(quantity):
subtotal = quantity * PRICE_PER_ITEM
return subtotal * (1 + VAT_RATE)
quantity = int(input("How many items? "))
print("Total to pay:", round(total_with_vat(quantity), 2))Worked example
Worked example: what happens when the program runs
Suppose the code above is saved as order.py and you type python order.py, then enter 3.
| 1. Start | The operating system creates a new process, gives it memory and starts the Python interpreter (itself a compiled program) inside it. |
|---|---|
| 2. Compile | CPython reads order.py from disk, checks that it is valid Python and compiles it to bytecode. A missing bracket or colon would be reported here as a syntax error, before anything runs. |
| 3. Define | The interpreter runs the bytecode from the top. It stores 12.5 and 0.2 in memory under the names PRICE_PER_ITEM and VAT_RATE, and records the function total_with_vat without running it yet. |
| 4. Input | input() asks the OS to read from the keyboard. The process waits, using no CPU, until you type 3 and press Enter. int() turns the text "3" into the number 3. |
| 5. Calculate | The function runs: 3 times 12.5 is 37.5, then 37.5 times 1.2 is 45.0. Each step is a handful of bytecode instructions, each of which becomes many machine instructions on the CPU. |
| 6. Output | print() asks the OS to show Total to pay: 45.0 on the screen. |
| 7. Finish | The program reaches its last line, the process ends and the OS reclaims its memory. Nothing was saved to disk, so the result is gone unless you copied it. |
Even this nine-line program relies on the operating system for memory, keyboard and screen, and on a language runtime to translate it for the CPU.
If you typed three instead of 3, int() would fail with a runtime error and the program would stop: the code never said what to do with bad input.
The program keeps money in a floating-point number to stay short. Real financial systems store whole pence or use a decimal type, for reasons the next section explains.
Bugs: when the instructions are wrong
A bug is a mistake in software that makes it behave differently from what was intended. Because computers follow instructions exactly, almost every bug is a gap between what the programmer meant and what the programmer actually wrote, or a situation nobody thought about.
It helps to recognise three kinds. A syntax error breaks the language's grammar (a missing bracket), so the compiler or interpreter refuses to run the program at all; these are found immediately. A runtime error happens while the program runs, such as dividing by zero or trying to read a file that does not exist; the program usually stops with an error message. A logic error is the most dangerous: the program runs happily and gives the wrong answer, for example charging VAT twice. Nothing crashes, so only testing, careful review or an unhappy customer will reveal it.
Professional teams expect bugs and build defences: automated tests, code review, gradual releases and monitoring that notices when error rates rise. Many of these defences are architectural, and they appear throughout this course.
Why this matters for architecture
Architecture is about the big decisions that are expensive to change, and several of them rest on the ideas in this section. The choice of language and runtime affects start-up time, memory use, which libraries you can use and whom you can hire. The split between memory and disk decides what must be stored durably, and how a system recovers after a crash. Processes and threads are the units that run, fail and scale: a later chapter asks whether a system should be one process or many cooperating ones. And the operating system's layered design, hiding hardware behind a stable set of requests, is the first instance of a principle you will meet again as information hiding.
The next section looks at what programs actually work on: data, and the structures and algorithms that make working with it fast or slow.
Check your understanding
Why does a program's data disappear when it crashes, unless it was saved?
While a program runs its data lives in RAM, which belongs to the process and is cleared when the process ends or the machine restarts. Only data written to storage such as a disk or database survives.
What is the difference between a compiler and an interpreter?
A compiler translates the whole program into another form (machine code or bytecode) before it runs; an interpreter carries out the instructions as it reads them. Many languages, such as Java and Python, compile to bytecode first and then run that bytecode in a virtual machine.
Why can a Java program built on a Windows laptop usually run unchanged on a Linux server?
Java is compiled to bytecode, not to machine code for one CPU. Any machine with a Java Virtual Machine installed can run that bytecode.
Why do programs ask the operating system to read a file or send data, instead of controlling the disk or network card themselves?
The operating system shares hardware safely between programs, enforces permissions and hides the details of each device behind system calls, so applications do not need to know which hardware they are running on.
Sources and further reading
Have a question about this section? ArchitectBot, the course’s AI study assistant, answers from the course first.
Ask: “If Python is slower than C, why do so many companies build their back-end systems in Python?”