Software Development 101

    What happens when you tap Buy?

    shop.example.com
    Shop Electronics

    Mechanical Keyboard

    $89.00

    Map of the system: laptop, Android phone, iPhone and desktop run web, native mobile, cross-platform and desktop apps, which call the API over HTTP. The API talks to the database with SQL, both on a server, inside Docker. Developers reach the server through the terminal over SSH and deploy from Git.

    One tap crossed all of this.

    You've written HTML, CSS and C, and SQL is coming next. Today is about the lines between them.

    What you'll learn

    • Follow one orderFrom the tap, through the API, into the database and back.
    • Read a requestMethods, status codes and JSON, and where to see them.
    • See why each part existsWhy frontend, API and database are kept separate.
    • Find your way around a serverWhere apps run, the terminal, SSH, basic Linux and nginx.
    • Follow a change to usersGit, code review, Docker and deployment.
    • Find your placeWho does what on a team, and where you start.

    You won't master these today. You'll see how they connect, and there's a quiz at the end.

    Follow one order

    • Web appOur React code runs in your browser and turns the tap into a request.
    • HTTPThe request crosses the internet to our server.
    • APINode.js checks the order and decides what to do.
    • DatabasePostgres saves it, so it's still there tomorrow.
    • The way backThe answer returns and the page updates.

    The frontend is three languages

    • HTMLWhat's on the page
    • CSSHow it looks
    • JavaScriptWhat it does

    You know the first two. JavaScript is what connects your page to everything else.

    Take the button apart

    shop.example.com

    Mechanical Keyboard

    $89.00

    Nothing is on yet, so there's no code to show.

    You can already read JavaScript

    C

    
    int total(int price, int qty) {
      return price * qty;
    }
    

    JavaScript

    
    function total(price, qty) {
      return price * qty;
    }
    
    • Same braces, loops and if. No types to declare, and no malloc.
    • It can change the page while people are using it.
    • It can talk to a server without reloading the page.
    • And there's no gcc step. So how does it run?

    Two ways code gets to run

    Compiled ahead of time
    shop.c
    Compiler
    shop.exe
    CPU runs it

    You ship a binary, built for each operating system and CPU.

    Run by a runtime
    shop.js
    Browser or Node.js
    Runs right away

    You ship the source. Anything with the runtime can run it.

    The line is blurry: V8, the engine in Chrome and Node.js, compiles JavaScript while it runs. TypeScript and Next.js add a build step before that. C# and Java sit in between, compiling to bytecode that a runtime then runs.

    Why our team uses React

    
    function BuyButton({ productId }) {
      const [status, setStatus] = useState("idle");
    
      async function buy() {
        setStatus("sending");
        await fetch("/api/orders", {
          method: "POST",
          body: JSON.stringify({ productId, quantity: 1 }),
        });
        setStatus("done");
      }
    
      return (
        <button onClick={buy} disabled={status === "sending"}>
          {status === "done" ? "Order placed" : "Buy"}
        </button>
      );
    }
    
    • A component is a function that returns UI. Change its state and React updates the page.
    • Next.js builds on React, adding pages, routing and server rendering.

    Now the request leaves your browser

    The frontend's job ends with a message. It's plain text, and you can read all of it.

    
    POST /api/orders HTTP/1.1
    Host: shop.example.com
    Content-Type: application/json
    
    {"productId":3,"quantity":1}
    

    Next stop: HTTP

    Every request gets one response

    Request

    
    POST /api/orders HTTP/1.1
    Host: shop.example.com
    Content-Type: application/json
    
    {"productId":3,"quantity":1}
    

    Response

    
    HTTP/1.1 201 Created
    Content-Type: application/json
    
    {"orderId":194,"total":"89.00"}
    
    • The first line says what you want, or how it went.
    • Headers describe the message. The body carries the data, usually as JSON.
    • The server forgets you between requests, so each one has to say who you are.

    Send a real request

    Press F12 and open the Network tab to watch these leave the browser.

    Request

    Choose a request.

    Response

    Nothing sent yet.

    The words you'll see every day

    Methods: what you want to do
    GETRead something
    POSTCreate something
    PUT, PATCHChange something
    DELETERemove something
    Status codes: how it went
    2xxIt worked: 200 OK, 201 Created
    3xxLook somewhere else
    4xxYour request is wrong: 401, 403, 404
    5xxThe server failed: 500, 503

    HTTPS sends these same messages, encrypted. That's the lock in your address bar.

    Why not go straight to the database?

    • The browser is the customer's computer. They can edit anything in it, even the price.
    • A database password sent to a browser is a password anyone can read.
    • Web, mobile and desktop all follow one set of rules when those rules live in one API.

    An API route in Node.js

    
    app.post("/api/orders", async (req, res) => {
      const { productId, quantity } = req.body;
    
      if (!Number.isInteger(quantity) || quantity < 1) {
        return res.status(400).json({ error: "Invalid quantity" });
      }
    
      const { rows } = await db.query(
        "SELECT price FROM products WHERE product_id = $1",
        [productId],
      );
      if (rows.length === 0) {
        return res.status(404).json({ error: "Product not found" });
      }
    
      const order = { productId, quantity, price: rows[0].price };
      const orderId = await saveOrder(req.user.id, order);
      res.status(201).json({ orderId });
    });
    
    • Read the request.
    • Check it. Nothing from outside is trusted.
    • Get the price from the database, not from the browser.
    • Answer with a status and JSON.

    The same job in other languages

    Python, with FastAPI

    
    @app.post("/api/orders", status_code=201)
    def create_order(order: OrderIn):
        product = find_product(order.product_id)
        if product is None:
            raise HTTPException(404, "Product not found")
        return {"orderId": save_order(order, product)}
    

    PHP, with Laravel

    
    Route::post('/api/orders', function (Request $req) {
        $product = Product::findOrFail($req->productId);
        $order = saveOrder($product, $req->quantity);
    
        return response()->json(
            ['orderId' => $order->id], 201);
    });
    

    Route, check, database, respond. Learn those steps once and they carry over.

    Why a database?

    • It survives. Restart the API and everything in its memory is gone, but your orders aren't.
    • Thousands of people can write at once without overwriting each other.
    • You ask for what you want in SQL, instead of writing the loops yourself.
    • The rules sit with the data. Postgres refuses a negative price.

    Your order becomes rows

    
    BEGIN;
    
    INSERT INTO orders (customer_id, order_date)
    VALUES (7, CURRENT_DATE)
    RETURNING order_id;  -- 194
    
    INSERT INTO order_items
      (order_id, product_id, quantity, unit_price)
    VALUES (194, 3, 1, 89.00);
    
    COMMIT;
    
    products
    product_idnameprice
    3Mechanical Keyboard89.00
    orders
    order_idcustomer_idstatus
    1947completed
    order_items
    order_idproduct_idquantityunit_price
    1943189.00

    BEGIN and COMMIT make it all or nothing: you never get an order with no items.

    Asking questions: SQL 101 starts here

    
    SELECT p.name, oi.quantity, oi.unit_price
    FROM order_items oi
    JOIN products p ON p.product_id = oi.product_id
    WHERE oi.order_id = 194;
    
    namequantityunit_price
    Mechanical Keyboard189.00
    • Never paste user input into SQL text. That's why the API route uses $1: it blocks SQL injection.
    • MySQL and SQLite speak SQL too. MongoDB and Redis work differently, for different jobs.

    The past: one program did everything

    
    <h1>Your orders</h1>
    <?php
    $rows = $db->query("SELECT * FROM orders");
    
    foreach ($rows as $row) {
      echo "<p>Order #{$row['order_id']}</p>";
    }
    ?>
    
    • PHP mixed HTML, logic and SQL in one file.
    • C# with Microsoft SQL Server followed the same pattern on Windows.
    • The server built each finished page, and most clicks reloaded it.
    • A phone app couldn't reuse any of it: the output was pages, not data.

    Plenty of this still runs today, so you may well meet it. It's simple, and it worked.

    Now: separate parts that agree on HTTP

    Web, mobile and desktop share one API, and each part can change and ship on its own.

    One order, start to finish

    • React, in your browserfetch("/api/orders")
    • HTTP, over the internetPOST /api/orders
    • Node.js, on the serverapp.post("/api/orders", …)
    • SQL, in PostgresINSERT INTO orders …
    • JSON, then React again201 {"orderId":194}

    Mobile: one app per OS, or one for both

    Android

    • KotlinJava in older apps
    • Android StudioShips on Google Play

    In between: one codebase

    • FlutterDart. Draws its own UI.
    • React NativeReact and JavaScript, with native UI.
    • CapacitorYour web app inside a native shell.
    • And others.NET MAUI, Kotlin Multiplatform

    iOS

    • SwiftObjective-C in older apps
    • XcodeNeeds a Mac. Ships on the App Store.

    Whichever you pick, the app still calls the same API.

    Desktop: the same choice

    Windows

    • C# and .NETC++ in older apps
    • Visual StudioShips as an installer or on the Microsoft Store

    In between: one codebase

    • ElectronA web app bundled with its own browser. VS Code and Slack use it.
    • TauriA web UI using the OS's web view, with Rust behind it.
    • And othersFlutter, Qt

    macOS

    • SwiftObjective-C in older apps
    • XcodeShips as a download or on the Mac App Store

    Your web skills reach further than the browser.

    The app is built. Where does it run?

    A desktop computer with a keyboard and mouse on a desk.
    Start on your own PCFree, and great for learning. But when it's off or your home internet drops, the shop is gone.
    Rows of servers and network cables in a data center.
    Move to a data centerBuildings full of servers that stay on, with backup power, cooling and very fast networks.

    Photos: Zarak Khan and Taylor Vick, on Unsplash

    In practice, we rent a VM

    One physical server, shared
    Your VM Someone else's Someone else's
    VPS
    A VM for a simple monthly price
    Cloud
    VMs plus many other services, billed by use
    • Security: they protect the building, the hardware and much of the network. Securing your VM is still your job.
    • Infrastructure: power, cooling and broken disks are their problem.
    • Speed: gigabit internet or faster, and a public IP address.
    • Room to grow: need more? Resize it or add another in minutes.

    What you actually get: a command line

    $ ssh root@203.0.113.10
    Welcome to Ubuntu 24.04 LTS
    root@vm:~#
    • You rent a VM and get an IP address and a login.
    • No desktop, no windows, no mouse. Just a prompt.
    • Installing, running and fixing your app all happen by typing.

    So before anything else, you need the terminal.

    The terminal: type instead of click

    $ pwd
    /home/intern/shop
    $ ls
    api docker-compose.yml web
    $ cd api
    $ grep -rn "api/orders" src
    src/routes.js:12:app.post("/api/orders", …
    $ npm start
    API listening on port 3000
    • Most servers have no desktop to click on.
    • Commands can go in a script, so a job you did once can run forever.
    • Git, Docker, npm and SSH are all used from here.

    SSH: a terminal on another computer

    $ ssh intern@server.example.com
    Welcome to Ubuntu
    intern@server:~$ hostname
    server
    • Everything you type is encrypted on the way.
    • You log in with a key pair. The public key goes on the server.
    • The private key stays on your laptop. Never send it to anyone.

    A server is just a computer

    Your programs, running as processes
    nginx Node.js Postgres
    Linux kernel
    Shares the hardware between processes
    Hardware
    CPU Memory Disk Network
    • Bare metal means Linux installed directly on a physical machine.
    • A cloud VM is a slice of a shared machine. Over SSH it feels the same.
    • Ubuntu and Debian are distributions: the kernel plus tools and an installer.

    Basic Linux: look around

    server$ nproc
    16
    server$ grep MemTotal /proc/meminfo
    MemTotal: 65536000 kB
    server$ df -h /
    Filesystem Size Used Avail Use% Mounted on /dev/sda1 916G 120G 750G 14% /
    server$ ps -C node,postgres -o pid,user,comm
    PID USER COMMAND 982 postgres postgres 1204 shop node
    • /home is your files, /etc is settings, /var/log is logs.
    • Every file and process has an owner. sudo runs one command as root, the user who can do anything.
    • There's no recycle bin. After rm, the file is gone.

    Deploying by hand

    server$ sudo apt install nodejs postgresql nginx
    server$ node --version
    v18.19.1
    server$ git clone git@git.example:shop.git
    server$ cd shop && npm ci --omit=dev
    server$ sudo systemctl enable --now shop-api
    # Next release
    server$ git pull && npm ci --omit=dev
    server$ sudo systemctl restart shop-api
    • apt installs software, in whatever version this Linux release ships.
    • systemd starts your app at boot and can restart it after a crash.
    • To deploy is to put new code on the server and restart it. The rest is detail.

    nginx: the front door

    Browser
    nginx :443
    Node.js :3000
    Postgres
    
    server {
        listen 443 ssl;
        server_name shop.example.com;
        # certificate paths go here
    
        root /var/www/shop;
    
        location /api/ {
            proxy_pass http://127.0.0.1:3000;
        }
    }
    
    • It handles HTTPS, so Node.js doesn't have to.
    • It serves the website's files itself and passes /api/ to Node.js.
    • Node.js listens only on 127.0.0.1, so the internet can't reach it directly.
    • sudo nginx -t checks the settings before you reload.

    All of this was set up by hand. Do it again on a second server, and the two slowly stop matching.

    "It works on my machine"

    Your laptopThe server
    OSWindowsLinux
    Node.js2218
    Postgres1614
    • Same code, different result, and hours lost finding out why.
    • A container packs the app with the runtime it needs, so it runs the same everywhere.

    From recipe to running app

    You know this
    shop.c
    shop.exe
    A running process
    Docker
    Dockerfile
    Image
    A running container
    
    FROM node:22-slim
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci --omit=dev
    COPY . .
    CMD ["node", "server.js"]
    
    • docker build makes the image. docker run starts a container from it.
    • One image can run as many containers as you need.

    Compose runs the whole system

    
    services:
      api:
        build: ./api
        ports:
          - "3000:3000"
        environment:
          DATABASE_URL: postgresql://shop:${DB_PASSWORD}@db:5432/shop
        depends_on:
          - db
    
      db:
        image: postgres:16
        environment:
          POSTGRES_USER: shop
          POSTGRES_PASSWORD: ${DB_PASSWORD}
        volumes:
          - pgdata:/var/lib/postgresql/data
    
    volumes:
      pgdata:
    
    $ docker compose up
    • The API reaches the database by name: db.
    • The volume keeps the data when containers are replaced.
    • SQL 101 starts its database in exactly this way.

    Five minutes on a server

    $ ssh intern@server.example.com
    intern@server:~$ docker compose ps
    NAME IMAGE STATUS shop-api-1 shop-api Up 2 hours shop-db-1 postgres:16 Up 2 hours
    intern@server:~$ docker compose logs --tail 2 api
    api-1 | POST /api/orders 201 34ms api-1 | POST /api/orders 404 8ms
    intern@server:~$ exit

    Terminal, SSH, Docker and HTTP status codes, all in one everyday task.

    Git remembers every change

    main add-buy-button
    $ git switch -c add-buy-button
    $ git add src/BuyButton.jsx
    $ git commit -m "Add Buy button"
    $ git push -u origin add-buy-button
    • A commit is a saved snapshot, with who made it and why.
    • A branch lets you work without touching what everyone else is using.
    • If a change breaks something, you can see exactly what changed and undo it.

    How your change reaches users

    • Branch and commitYour work, on your own branch.
    • Pull request"Please add this to main."
    • Automated checksTests and builds run on their own.
    • Code reviewA teammate reads it and comments.
    • MergeIt joins main.
    • DeployNew containers start on the server.

    Working on a team

    • Keep changes smallA 50-line pull request gets a real review. A 2,000-line one gets a shrug.
    • Write down whyThe code shows what changed. Your commit message explains why.
    • Ask earlyStuck for 30 minutes? Say what you tried, then ask.
    • Review comments are about the codeEveryone gets them, including the most senior people.
    • Never commit a secretGit remembers everything, including the password you deleted later.
    • Read other people's codeIt's the fastest way to learn how this codebase works.

    The whole picture

    You've now seen every box and line. The Server box is the VM we rent.

    Everything on one page

    Building the app

    One tap on Buy

    Running and shipping it

    Every part of the map is someone's job

    • FrontendReact and Next.js: what people see and tap.
    • Mobile and desktopThe apps on phones and computers.
    • BackendThe API and its rules.
    • DataSchemas, queries and reports.
    • DevOpsContainers, servers and deploys.
    • QAFinding what breaks before users do.

    You don't have to pick yet. Full-stack developers cross these lines every day.

    So, what happens when you tap Buy?

    You can answer that now, and you know where to look when it breaks.

    • Next up: SQL 101.
    • Try this today: press F12 on any site and read the Network tab.
    • What are you still curious about?
    • Next slide: a quick quiz on everything here.