We ended the previous lesson with an open question: if the processor only understands ones and zeros, how have we ended up being able to write print("Hello") and have it work? The answer is a story spanning almost two centuries, in which each generation of programmers built a tool so they would not have to suffer what the previous one suffered.
This lesson is not a list of dates to memorise. It is a journey with a purpose: for each milestone we will ask what specific problem existed before and what became possible afterwards that had previously been impossible or unbearable. Understanding that chain has a very practical use: almost all the odd decisions you will come across in modern languages — why Python uses indentation, why functions exist, why nobody uses goto — are scars from specific battles. Whoever knows the battle understands the scar.
At the end we will look at the idea that unifies the whole journey, and that is the one thing you really need to take away from here.
Contents
- Before computers: Jacquard and Ada Lovelace
- Electromechanical machines and the first "bug"
- Machine language and assembly: talking to the processor
- The first high-level languages: FORTRAN, COBOL and LISP
- Structured programming and the end of
goto - Object orientation: Smalltalk, C++ and Java
- Dynamic languages and the web: Python, JavaScript and PHP
- The present era: mobile, cloud, open source and AI assistants
- Chronological table and timeline
- The key idea: layers of abstraction
- Common mistakes and tips
- Exercises
- Conclusion
- Before computers: Jacquard and Ada Lovelace
Programming is older than the computer. That comes as a surprise, but it is literally true.
The Jacquard loom (1804). In Lyon, Joseph-Marie Jacquard built a loom that wove complex patterns by following the instructions on a series of punched cards. Where there was a hole a needle passed through; where there was none, it did not. Changing the cards changed the pattern, without touching the machine.
There, already complete, is the fundamental concept of computing: one and the same machine does different things depending on the instructions it is given, and those instructions are data, separate from the machine. A computer is exactly that, with electronics instead of needles.
- Problem it solved: to change the pattern of a fabric, the loom had to be physically reconfigured, days of work for highly specialised craftsmen.
- What it enabled: the production of fabrics with complex patterns on an industrial scale, and the idea of a program as something interchangeable.
Ada Lovelace (1843). Charles Babbage designed the analytical engine, a general-purpose mechanical contraption that was never actually built. Ada Lovelace, a mathematician, translated an article about it and added notes of her own that ran to three times the length of the original. In the famous "Note G" she described a step-by-step procedure for the machine to compute the Bernoulli numbers, including the repetition of operations: what we would today call a loop.
That is why she is considered the first programmer. But her deepest contribution was conceptual: she was the first person to write that the machine could manipulate anything that could be represented with symbols, not just numbers — she explicitly mentioned the possibility of composing music. In other words, she saw that computing was not a branch of arithmetic but something far more general. It would take a century for her to be proved right.
- Electromechanical machines and the first "bug"
Between 1930 and 1950 the first genuinely working calculating machines arrive: electromechanical relays first, vacuum tubes later. Names such as the Harvard Mark I, the ENIAC or Konrad Zuse's Z3.
Programming those machines was physical work. On the ENIAC, changing program meant repositioning cables and switches for days. There was no code: there was topology. The team doing that work was made up largely of women mathematicians — Kay McNulty, Betty Jennings, Marlyn Meltzer and others — whose labour was for decades regarded as auxiliary technical work rather than programming.
From this period comes an anecdote that has given half the trade its name. In 1947, Grace Hopper's team at Harvard traced a fault on the Mark II and found that the cause was a moth trapped between the contacts of a relay. They taped it into the logbook with the note "First actual case of bug being found". The term bug for a fault was already in use in engineering, but this is the anecdote that fixed it in computing.
Grace Hopper did something far more important than finding a moth: she argued, against the majority opinion of her time, that programs could be written in something resembling English and that another program could translate them automatically. She was told that computers did not understand English. She built the first compiler in 1952 and carried on.
- Problem it solved: programming required handling the machine physically and knowing its electronics.
- What it enabled: the idea that translation from human language to machine language can be automated. It is the gateway to everything that follows.
- Machine language and assembly: talking to the processor
With stored-program computers, the program stops being wiring and becomes data in memory: binary sequences that the processor reads and executes. It is the machine language we saw in the previous lesson.
Writing directly in binary is as painful as it sounds. The first abstraction, almost immediate, was assembly language: giving each processor instruction a short, memorable name.
Instead of writing this:
...you write this:
MOV AL, 61h ; put the value 61 (hexadecimal) into register AL
ADD AL, 5 ; add 5 to whatever is in ALA program called an assembler does the conversion, which is practically one to one: each line of assembly corresponds to one machine instruction.
The gain in readability is enormous, but the limitations are still there:
-
Assembly is different for each family of processors. A program written for one does not run on another: it has to be rewritten from scratch.
-
It still forces you to think like the machine — registers, memory addresses — rather than like the problem.
-
An operation that is mentally one line ("work out the average of these numbers") is dozens of instructions.
-
Problem it solved: the utter unreadability of binary.
-
What it did not solve: portability between machines and the distance between the problem and the code.
- The first high-level languages: FORTRAN, COBOL and LISP
The 1950s bring the decisive leap: languages in which you write the idea, not the processor instruction. Three appear, very different from one another, and all three are still alive in some form.
FORTRAN (1957), by John Backus at IBM. Designed for scientific and engineering computation. Its proposal was to allow mathematical formulas to be written almost as they are:
Nobody believed an automatic translator could generate code as efficient as that of a good assembly programmer. FORTRAN proved it could, and that was its real triumph: it convinced the industry that high-level languages were seriously viable.
- Problem it solved: scientists were losing months translating formulas into assembly instead of doing research.
COBOL (1959), with Grace Hopper among its promoters. Aimed at business administration: payroll, invoicing, banking. Its bet was a verbose, English-like syntax, readable by non-technical people:
That verbosity was laughed at for decades. Sixty-five years later, a good part of the world's banking and insurance systems still run COBOL, and finding someone to maintain it is a genuine industry problem.
- Problem it solved: companies needed to process large volumes of administrative data with programs someone could read and audit years later.
LISP (1958), by John McCarthy. Born in the field of artificial intelligence, with a radical idea: program and data have the same shape, lists. That allows a program to manipulate programs.
LISP introduced or popularised ideas that are everywhere today: recursion, functions that take other functions, automatic memory management. It is the grandfather of the functional paradigm that we will see, in its modern form, in the lesson Functions as values.
- Problem it solved: expressing symbolic computations and flexible data structures, impossible to fit into FORTRAN's numeric mould.
| Language | Year | Field | Breakthrough idea |
|---|---|---|---|
| FORTRAN | 1957 | Science and engineering | Writing mathematical formulas directly |
| COBOL | 1959 | Business administration | Code readable by non-programmers |
| LISP | 1958 | Artificial intelligence | Code and data with the same structure |
- Structured programming and the end of
goto
gotoThe 1960s bring a new and unpleasant problem. Programs are now large, and they have become impossible to understand.
The main cause was the goto instruction, which jumps to any point in the program. Used without discipline it produces what came to be known as spaghetti code: to find out how a line is reached you have to trace jumps all over the file, and any change breaks something three screens further down.
In 1968, Edsger Dijkstra published a letter titled "Go To Statement Considered Harmful" that ignited the debate out of which structured programming emerged. His thesis: any program can be written by combining just three structures.
flowchart LR
subgraph S["Sequence"]
A1["Step 1"] --> A2["Step 2"] --> A3["Step 3"]
end
subgraph D["Selection"]
B1{"Condition?"} -->|Yes| B2["Path A"]
B1 -->|No| B3["Path B"]
end
subgraph I["Iteration"]
C1{"Carry on?"} -->|Yes| C2["Repeat"] --> C1
C1 -->|No| C3["Exit"]
end
- Sequence: one instruction after another.
- Selection: choosing between paths according to a condition (
if...then). - Iteration: repeating while something holds (loops).
With those three pieces, plus functions to group code together, goto stops being necessary. ALGOL (1958-1968) was the laboratory for these ideas and the syntactic ancestor of almost everything that came afterwards. Pascal (1970) took them into education. C (1972), by Dennis Ritchie at Bell Labs, combined them with low-level access to the hardware and became the language in which Unix, Linux, Windows and, later, the Python interpreter itself were written.
- Object orientation: Smalltalk, C++ and Java
Structured programming organised the program's flow. Something else was left unresolved: organising the data and its relationship with the code that manipulates it.
In a large structured program, the data is on one side (loose variables) and the functions on the other. Nothing stops a function from touching data that is none of its business, and once the program grows nobody knows which part modifies what.
Object-oriented programming proposes packaging together the data and the operations that act on it. A "task" object holds its title, its assignee and its priority, and also knows how to mark itself as completed.
-
Simula 67 (Norway) introduced classes and objects for simulations.
-
Smalltalk (Xerox PARC, 1970s) took the idea to the extreme: everything is an object that communicates with others through messages. It was also the birthplace of the graphical interface with windows and a mouse.
-
C++ (1983, Bjarne Stroustrup) added objects to C without giving up performance. A whole generation of desktop software and video games was written with it.
-
Java (1995, Sun Microsystems) bet on "write once, run anywhere": the code is compiled to an intermediate format called bytecode, which is run by a virtual machine present on every system. We will look at that mechanism in detail in the next lesson. Java became the standard for enterprise software.
-
Problem it solved: in large programs, keeping data and the code that uses it together, and being able to reuse components without copying them.
-
Where you will see it: module 7 builds EasyTask with classes and objects.
- Dynamic languages and the web: Python, JavaScript and PHP
The 1990s change the priorities. With ever faster machines, the scarce resource stops being the processor cycle and becomes the time of the person doing the programming. And the internet appears, demanding that things be built and published very quickly.
This is how dynamic languages are born: more flexible, with no need to declare the type of each piece of data, interpreted, with a great deal solved out of the box.
- Python (1991, Guido van Rossum). Explicitly designed to be readable. It forces you to indent code, forbids most flourishes and follows the principle that there should be one obvious way to do each thing. It began as a scripting language and today dominates data science, automation and artificial intelligence.
- JavaScript (1995, Brendan Eich, in ten days). Created to give web pages interactivity inside the browser. Against all expectations — and despite an initial design full of oddities — it became the most widely used language in the world and today also runs on servers.
- PHP (1995). Designed to generate web pages on the server. It underpinned the growth of the web in the 2000s; WordPress, which powers a huge share of the world's sites, is written in PHP.
Compare the price of showing some text on screen across three eras:
// Java (1995): you have to declare a class and a method
public class Hello {
public static void main(String[] args) {
System.out.println("Hello");
}
}All three do the same thing. The difference is not one of power, it is one of how much machine the writer has to keep in their head.
- Problem it solved: the cost in human time of writing and maintaining software, and the need to iterate quickly on the web.
- The present era: mobile, cloud, open source and AI assistants
The last twenty years do not bring a revolution in languages comparable to the earlier ones, but they do radically change the context in which programming happens.
- Mobile (2007 onwards). The iPhone and Android create a new market. Swift (Apple, 2014) and Kotlin (2011, official on Android since 2017) appear, modern languages that correct design flaws inherited from Objective-C and Java.
- The cloud. Code stops running on "a computer" and starts running on rented, elastic infrastructure. That makes skills that used to belong to systems administration critical: deployment, containers, scaling.
- Open source. Open code goes from militant curiosity to the foundation of the industry: Linux, Python, PostgreSQL, Git. Today it is normal for a commercial project to rest on hundreds of libraries written by strangers and given away.
- New languages with specific obsessions. Go (2009) prioritises simplicity and concurrency; Rust (2010) eliminates by design a whole family of memory errors without giving up performance.
- AI assistants (2021 onwards). Tools capable of generating code from a description in natural language. They change the mechanical side of the trade: writing boilerplate code gets cheaper all the time.
On that last point a smoke-free note is in order, because it bears directly on why you are taking this course. An assistant writes plausible code very easily. What it cannot do for you is:
- Decide what needs to be built. Marta does not know how to state her problem in precise terms; that job is section 8 of the previous lesson, and it is human.
- Verify that what was generated is correct. To know whether a program does what it should, you have to understand it.
- Diagnose it when it fails. Accepting code you do not understand means being left without resources the moment it stops working.
Put another way: AI has raised the bar on the mechanical part and made the fundamentals more valuable, not less. It is exactly what has happened at every previous layer of abstraction.
- Chronological table and timeline
| Decade | Milestone or language | Problem it solved |
|---|---|---|
| 1800 | Jacquard loom | Physically reconfiguring the machine for each pattern |
| 1840 | Ada Lovelace's notes | Nobody had described an algorithm for a general-purpose machine |
| 1940 | ENIAC, Mark I, first bug | Massive computation impossible by hand; debugging is born |
| 1950 | Assembly and the first compiler | Unreadability of binary; automatic translation |
| 1950-60 | FORTRAN, COBOL, LISP | Distance between the problem (formula, payroll, symbol) and the machine |
| 1960-70 | ALGOL, structured programming | Spaghetti code impossible to maintain |
| 1970-80 | C, Pascal, Smalltalk | Portability, teaching, and organising data + code |
| 1980-90 | C++, Java | Reuse in large programs; running on any system |
| 1990-2000 | Python, JavaScript, PHP | Cost in human time; speed of web development |
| 2000-2010 | Mobile, open source, cloud | New platforms and worldwide collaboration |
| 2010-2020 | Go, Rust, Swift, Kotlin | Concurrency, memory safety, ergonomics |
| 2020- | AI assistants | Cost of writing routine code |
timeline
title Timeline of programming
1804 : Jacquard loom (punched cards)
1843 : Ada Lovelace writes the first algorithm
1945 : ENIAC and the first women programmers
1952 : Grace Hopper builds the first compiler
1957 : FORTRAN, first successful high-level language
1959 : COBOL for business administration
1968 : Dijkstra and structured programming
1972 : C and the Unix system
1983 : C++ takes objects to the industry
1991 : Python puts readability first
1995 : Java and JavaScript
2008 : Explosion of mobile and the cloud
2010 : Go and Rust
2021 : AI assistants for programming
- The key idea: layers of abstraction
If you take one sentence away from this lesson, let it be this one: every layer of abstraction exists so that the person programming thinks about the problem and not about the machine.
Look at the whole journey through that lens:
| Layer | What stopped being a concern |
|---|---|
| Punched cards | Rebuilding the machine for each task |
| Assembly | Remembering binary codes |
| High-level languages | Registers and the processor's quirks |
| Structured programming | Tracing jumps all over the program |
| Object orientation | Which part of the code touches which data |
| Automatic memory management | Allocating and freeing memory by hand |
| Libraries and open source | Rewriting what is already solved |
| Cloud | Buying and administering servers |
| AI assistants | Typing out routine code |
No layer eliminated the work: it pushed it upwards, towards the problem. The programmer of 1955 wondered how to move a value between registers. The programmer of 2026 wonders whether Marta should be allowed to reassign an already completed task. It is exactly the same profession, answering questions that are ever closer to the person who has the problem.
One practical consequence: you do not need to know every lower layer to work at the top one, but knowing they exist saves you when something behaves unexpectedly. There is no magic underneath print("Hello"). There is history.
Common Mistakes and Tips
Memorising dates instead of understanding transitions. Nobody is going to ask you the year of COBOL. What does matter is being able to explain why it appeared: companies needed to process administrative data with auditable code. The date is a label; the problem solved is the content.
Believing the old stuff is dead. COBOL moves banking transactions today. C sits underneath Linux, Windows and the Python interpreter you will use tomorrow. FORTRAN is still in scientific computing. In software, "old" usually means "battle-tested for forty years".
Believing the new replaces the old. It almost never happens: it accumulates. Object orientation did not eliminate structured programming, it absorbed it; in Python you will use both at once and will not even notice the switch.
Thinking history is decoration. When in module 3 you read that you should avoid loops with odd jumps, or in module 8 that code must be readable by another person, you will be applying conclusions from specific debates you can date. Knowing that turns an arbitrary rule into a reasoned decision.
A tip: when you learn a new construct, ask yourself "what did people have to do before this existed?". Almost always the answer is "something far more laborious and error-prone", and that is where the construct starts to make sense.
Exercises
Exercise 1: Each milestone with its problem
Match each advance with the problem it came to solve, and explain in one or two sentences what used to happen before:
| Advance | Problem it solved |
|---|---|
| a) Assembly | 1. Large programs impossible to follow because of jumps |
| b) FORTRAN | 2. Writing programs in pure binary |
| c) Structured programming | 3. Data and code scattered across very large programs |
| d) Object orientation | 4. Scientists translating formulas by hand into machine instructions |
Exercise 2: The same task in three eras
Alba Studio wants to show the message Alba Studio - EasyTask on screen. Describe, without writing real code, what a programmer would have to do to achieve it at three historical moments: (a) programming the ENIAC in 1946, (b) in assembly in 1960, (c) in Python today. In each case, state what knowledge they needed and roughly how long it would take them.
Exercise 3: Spotting the layer of abstraction
For each situation, say which layer of abstraction is working for you and what you would have to do manually if it did not exist:
- You write
print("Hello")and do not worry about which screen position each letter is drawn at. - You create a list of tasks and neither allocate memory for it nor free it when you are done.
- Your program works the same on Luis's laptop (Windows) and on Nuria's (macOS).
- You use a library to read a CSV file instead of interpreting commas and quotes yourself.
Solutions
Solution 1.
- a) Assembly → 2. Before, binary sequences had to be written by hand, counting bits and consulting opcode tables. A single wrong bit produced a fault impossible to locate by reading the program.
- b) FORTRAN → 4. Before, a physicist wanting to compute a trajectory had to translate each formula into dozens of machine instructions, spending more time as a translator than as a physicist.
- c) Structured programming → 1. With undisciplined
goto, working out how a line was reached required tracing jumps all over the file; any change broke behaviour far away. - d) Object orientation → 3. In a program of a hundred thousand lines, with global variables and loose functions, it was impossible to know which part of the code modified which piece of data.
Solution 2.
(a) ENIAC, 1946. The concept of "showing text" does not exist: the output is punched cards or lights. Each character would have to be represented numerically and the machine physically configured — cables and switches — to produce that output. Knowledge: the electronics of that specific machine, mathematics, numeric representation. Time: days, and occupying the whole computer.
(b) Assembly, 1960. You write a sequence of instructions that loads the address of the text into a register, invokes the system routine to write to the output device and checks the result. The text has to be defined character by character. Knowledge: the instruction set of that specific processor, system call conventions. Time: between half an hour and a few hours, and it is no use on another machine.
(c) Python, today. One line: print("Alba Studio - EasyTask"). Knowledge: knowing that print exists and how quotes are written. Time: seconds.
The conclusion is not that we are cleverer today, but that the work of those decades is built into the tools we use.
Solution 3.
- The high-level language and the system libraries. Without them you would have to calculate screen positions or send each character to the output device with operating system calls.
- Automatic memory management (Python's garbage collector). Without it you would have to allocate memory explicitly and free it when finished; forgetting causes memory leaks, and freeing too much causes crashes.
- The Python interpreter and standard library, which hide the differences between operating systems. Without that you would have to compile and probably rewrite parts of the program for each system, as happened in the eighties.
- The open source library ecosystem. Without it you would have to write the CSV format parsing yourself, including awkward cases such as commas inside quotes or line breaks within a field.
Conclusion
The history of programming is a single idea repeated: every generation built a layer so as to stop thinking about the machine and think more about the problem. From Jacquard's cards to Ada Lovelace's first algorithm; from the ENIAC's wiring to Grace Hopper's compiler; from binary to assembly and from there to FORTRAN, COBOL and LISP; from spaghetti code to structured programming; from loose functions to objects; and from there to dynamic languages, the web, the cloud and today's assistants. No layer eliminated the work: it pushed it upwards, closer to the people who have the problem — closer to Marta and her lost paper notes.
That journey also explains why today there is not one programming language but hundreds, each carrying the decisions of the era and the field in which it was born. A language designed to compute trajectories does not resemble one designed for payroll, nor one designed for the browser. And at some point you have to choose one.
In the next lesson, Programming languages, we will see how those hundreds of languages are classified — by level of abstraction, by form of execution, by typing and by paradigm — we will compare the most widely used ones today in a practical table, and we will establish criteria for choosing the right one in each case. We will finish by justifying why Python is the correct choice for this course and for EasyTask.
Fundamentals of Programming
Module 1: Introduction to Programming
- What is programming?
- History of programming
- Programming languages
- Development environments
- From problem to algorithm
Module 2: Core Concepts
- Variables and data types
- Operators and expressions
- Input and output
- Type conversion and data validation
Module 3: Control Structures
Module 4: Functions and Procedures
- Defining and using functions
- Parameters and return values
- Variable scope
- Breaking a program down into functions
- Functions as values: lambda and higher order
Module 5: Data Structures
- Lists and arrays
- Strings
- Dictionaries and sets
- Tuples and nested structures
- Saving data to files: text, CSV and JSON
Module 6: Basic Algorithms
Module 7: Objects and Code Organisation
- From data to objects: classes and instances
- Attributes, methods and the constructor
- Collections of objects
- Modules, packages and imports
Module 8: Good Practices and Tools
- Documentation and comments
- Debugging and error handling
- Version control
- Automated testing
- Style, readability and refactoring
