Agent PCIS 2500 / 3990
CIS 3990 · Fall 2026

Lab 4

Read-only Lab specification

Sign in to Agent P
Instructions

Lab specification

Lab 4 — Codebase History and Architecture Diagram

Assigned: Tuesday, September 15 · Due: before Tuesday, September 22

Prerequisites: Labs 1–3 (component diagram from Lab 3 is an input; CLAUDE.md provides codebase context for agent sessions).

Prepares for: Project 2 (Feature Extension) — understanding where things are and how they evolved helps you find the right place to add something new.

Estimated time: 90–120 minutes with agentic tools.

Grading: Pass/fail.

In lecture, we have moved from the question of how LLMs work to how software can be designed at scale. "Software architecture" defines how we factor software problems into layered abstractions and components, with carefully defined interfaces. A good web site for learning about software architecture is the one by [Martin Fowler](https://martinfowler.com/architecture/).

What are some goals of good software architecture?

  1. Reuse: It supports shared implementations of common functionality.
  2. Abstraction: It lets engineers define building blocks from higher-level components and abstractions. For example, it's nice to think about the network as having streams of data coming in and going out –– rather than managing packets and headers.
  3. Encapsulation of properties like security: We can "bake in" certain constraints and properties into our modules.
  4. Decomposition into independent units: If we want multiple developers or agents –– or even one engineer who doesn't remember everything –– to be able to work on software, it's helpful to have a way of scoping sub-components in a way that has focused knowledge, interactions, and changes.

We can think both of doing this at the code level and at the data level (e.g., defining types, classes, and objects, or persistent data objects in a database). We can also provide interfaces and constraints over all of these software and data components: rules about concurrency, about side effects, and so on.

Architecture is an evolving thing, and is closely tied to code. Version control and branching/merging, version history, and ADRs are all mechanisms for helping manage this.

For this Lab, we will try to understand how tools can help you in understanding and developing software architecture. You'll want to download the Lab 4 submission Markdown file, create a lab4/ directory in your Spacebar project repo, and edit submission.md there.


Lab System Prompt

As usual: set up [AGENTS.md](AGENTS.md) as your CLAUDE.md (or paste it as a system prompt) if you haven't already — it applies to every lab this semester, not just this one.


Overview

A codebase's history is a record of decisions, mistakes, and priorities. Learning to read that history — and to produce an architecture diagram that captures layer boundaries, not just file structure — are core reverse-engineering skills. This lab applies both to Spacebar.

By the end you will have: a git history analysis verified against the actual commit log, a layer-level architecture diagram that builds on Lab 3's component map, an analysis of one poorly-defined module boundary, and an ADR for one codebase design choice. A commit-frequency chart rounds it out, turning the history you read in Part A into something you can see at a glance.


Part A — Git History Analysis

Spacebar has a particular architecture. It's difficult to quantify whether it's good or bad, but we can look at how it has supported evolution.

Goal: Read a codebase's past to understand its present.

The pinned Spacebar repository has roughly 5,600 commits stretching back to November 2020, so there is plenty of history to read. The --since filters below start in 2022 to keep the output manageable; widen them if you want the early period.

  1. Get a summary of the Spacebar commit history:
   git log --oneline --since="2022-01-01" | head -100
   git log --format="%ad %s" --date=short | head -100
   git shortlog -sn --since="2022-01-01" | head -20
  1. Ask Claude Code to analyze the history. You should be able to prompt along the lines of:
"Here is a sample of the Spacebar git log: [paste the output]. What were the major feature additions?"

Some other things to consider and ask about:

  • When were periods of heavy commit activity?
  • When were there periods of heavy bug fixing (and lots of commits in a row)?
  • What modules were touched most frequently?
  • Were there major components that are mentioned as having been deleted or discarded?
  1. Verify two specific claims the agent makes against git log. For example, if Claude says "the gateway was heavily refactored in early 2023," verify this by running:
   git log --oneline --since="2023-01-01" --until="2023-06-01" -- src/gateway/

For each claim: quote the agent's statement, quote the relevant git log output, and write one sentence on whether the claim holds.


Part B — Architecture Diagram

Goal: Transform Lab 3's component map into a layer-level architecture diagram. The Lab 3 map shows modules and communication paths; this diagram adds data flows, layer boundaries, and key interface contracts between layers. Do not redraw the same module inventory from scratch.

You have now drawn three diagrams. What's the difference?

By this point you have produced three pictures of Spacebar. Each answers a different question:

DiagramFromThe question it answers
Module diagramLab 1, docs/module-diagram.mmdWhat is in this repository? Directories and files, roughly as the filesystem lays them out.
Component diagramLab 3, lab3/spacebar-components.mmdWhat are the running pieces, and who talks to whom? Named components and their communication paths.
Architecture diagramThis lab, lab4/architecture.mmdWhere are the layer boundaries, and what crosses them? Responsibility bands, the direction of dependency, and the data format at each crossing.

The move from the second to the third is the interesting one. A component diagram can show you that src/api/ talks to PostgreSQL. An architecture diagram has to say through what — which layer owns the SQL, what shape the data has on each side, and whether anything is allowed to skip a layer.

The four layers

For this lab, let's consider four layers.

  • HTTP edgesrc/api/routes/ and src/api/middlewares/. Parses requests, authenticates, serializes responses. Owns no business rules.
  • Application logic — permission computation (src/util/util/Permissions.ts) and event dispatch (emitEvent()). Decides what a request is allowed to do and who else needs to hear about it.
  • Domain modelsrc/database/entities/. TypeORM entity classes; the types that have database tables behind them.
  • Persistence — PostgreSQL itself. Relational storage and foreign-key enforcement.

 

  1. Ask Claude Code for the diagram, and have it saved to lab4/architecture.mmd. Mermaid source files use the .mmd extension, not .md — they are diagram source, not documents. Ask:
"Draw a Mermaid graph LR architecture diagram of the Spacebar server at the layer level. Show: the HTTP edge (Express routes + middleware), the application logic (permission computation, event dispatch), the domain model (TypeORM entities), and the persistence layer (PostgreSQL). For each boundary crossing, label what data format is used (TypeScript objects, JSON, SQL, WebSocket frames, event bus messages)."
  1. Validate the diagram against the component diagram from Lab 3 and resolve any conflicts. Write a description under the Lab 3 Reconciliation heading in lab4/submission.md.

Write one annotation per conflict: what the two diagrams disagreed on, which one was right, and the source evidence that settles it. If you find no substantive conflicts, say so explicitly and instead name one thing the architecture view shows that the component map could not — a layer that turned out to be skipped, a data format that changes at a boundary, a dependency pointing the opposite direction from what you assumed. "No conflicts found" on its own does not pass; the section has to show you actually compared them.

  1. Compare the architecture to a standard 3-tier web application. "3-tier" is the oldest and most common way to carve up a server: a presentation tier that renders or serializes what the user sees, a logic tier that holds the business rules, and a data tier that stores state. The classic rule is that each tier talks only to its immediate neighbor — presentation never reaches past logic to touch the database directly.

Your four layers do not map one-to-one onto those three tiers, and working out where they do and do not line up is the point of the exercise. Where does Spacebar follow the pattern? Where does it deviate, and does the deviation look deliberate or accidental? Write a short paragraph (4–6 sentences) with your analysis under the 3-Tier Comparison heading in lab4/submission.md — it is graded separately from the diagram.

Commit lab4/architecture.mmd, then paste its full contents into `lab4/submission.md` under the Layer Diagram heading, inside a ``mermaid fence. The .mmd` file is the working artifact; the pasted copy is what gets graded — same arrangement as Lab 3's component diagram.


Part C — Poorly-Defined Module Boundary

Not every piece of software is consistently designed. Are there issues like this in Spacebar?

Goal: Identify one place where the architecture has a coupling problem.

  1. Look for a place in the codebase where two modules are more tightly coupled than their interface suggests — a module that imports from another's internals rather than its public API, or where responsibility is ambiguous.

Each src/ module publishes a public surface through a barrel file (its index.ts), which is what the @spacebar/util, @spacebar/database, and @spacebar/api aliases resolve to. An import that names the barrel is using the public API. An import with a path after the module name — @spacebar/util/util/ProcessLifecycle — is reaching past it into internals.

Candidates to investigate:

  • Reaching past a barrel. Find imports of the form @spacebar/<module>/<something>. Who does it, and what are they reaching for that the barrel does not expose? You can use the grep tool to match patterns in files, and head to return only the first few lines. Ask Claude Code to explain this line!
     grep -rnE 'from "@spacebar/[a-z]+/' src/ | head -30
  • Duplicated responsibility. Permission checks happen in both src/api/ and src/gateway/. Are they calling the same helper, or has one of them grown its own version?
     grep -rlE 'finalPermission|getPermission' src/api src/gateway
  • Circular imports at the module level. Build a picture of which top-level modules import which:
     for d in api gateway util database cdn schemas; do
       printf "%-10s -> " "$d"
       grep -rhoE 'from "@spacebar/[a-z]+' src/$d | sed 's/.*@spacebar\///' | sort -u | tr '\n' ' '
       echo
     done

Read the output as a graph. If X appears in Y's row and Y appears in X's row, those two modules import each other.

  1. Optionally, cross-check with madge, a tool that walks the import graph for you:
   npx madge --circular --extensions ts --ts-config tsconfig.json src/

Run correctly, it reports well over a hundred cycles, which is more alarming than it should be. Most of them route through a module's own index.ts barrel: the barrel re-exports a file, and that file imports something else from the barrel. That is a normal consequence of the barrel pattern, not a design defect. The cycles worth writing about are the ones that cross a module boundary — where util and database, or api and cdn, depend on each other — because those are the ones that would actually stop you from extracting a module or reasoning about it alone.

  1. Write a short paragraph (5–8 sentences) explaining:
  • What the boundary is and why it seems problematic.
  • What a cleaner decomposition might look like.
  • What would break if you tried to enforce the cleaner boundary today.

Part D — Architecture Decision Record

Goal: Document one design choice in the codebase using the ADR format.

  1. Choose one design choice visible in the codebase, and try to understand the associated code. Good candidates:
  • Why the permission logic lives in src/util/util/Permissions.ts rather than inside src/api/
  • Why TypeORM is used rather than raw SQL or a different ORM
  • Why emitEvent() uses an in-process event bus rather than direct function calls
  • Why the gateway and API are separate processes but share src/util/
  1. Write an ADR for this, using the standard format:
    # ADR-001: [Title]

    ## Status
    Accepted (inferred from codebase)

    ## Context
    [What problem was being solved? What constraints existed?]

    ## Decision
    [What was decided? Be specific — quote the relevant code or file structure.]

    ## Consequences
    ### Positive
    - [Benefit 1]
    - [Benefit 2]
    ### Negative / Trade-offs
    - [Cost or limitation]

Note: since you are inferring the ADR from an existing codebase rather than writing it before a decision, your "Context" section should explain what problem the design appears to have been solving, even if you can only infer it from the code.

Save as lab4/ADR-001.md.


Part E — Commit Frequency Visualization

Goal: Visualize how active development has been across the project's history, so the periods you identified in Part A become visible rather than inferred.

  1. Extract commit dates:
    git log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c > /tmp/commit-freq.txt
    cat /tmp/commit-freq.txt
  1. Ask Claude Code to generate a Python matplotlib bar chart:
"Here is a commit frequency table: [paste the output]. Write a self-contained Python script that produces a bar chart of commits per month, saves it as lab4/commit-frequency.png, and includes axis labels (x: month, y: commit count) and a title."
  1. Save the script Claude produces as lab4/plot-commits.py, then install matplotlib if you do not already have it and run the script:
    sudo apt install -y python3-matplotlib    # not present on a fresh Ubuntu install
    python3 lab4/plot-commits.py

Use apt rather than pip3 here. Recent Ubuntu marks its system Python as externally managed and refuses a system-wide pip3 install with an error: externally-managed-environment message. If you prefer pip, put it in a virtual environment first: python3 -m venv ~/.venvs/lab4 && source ~/.venvs/lab4/bin/activate && pip install matplotlib.

Confirm lab4/commit-frequency.png appears and the chart is readable. Commit both the script and the rendered image.

The chart is graded from the script, not the image — the grading model is text-only and never sees the PNG. So the script has to stand on its own: real commit counts rather than a hardcoded placeholder, an x-axis label, a y-axis label, and a title.


Deliverables

Start from the [Lab 4 submission template](lab-04-submission.md) for lab4/submission.md, which carries Parts A, B, and C; the ADR and the chart are separate files. (The template is also downloadable from the bottom of the Lab 4 assignment page on Agent P.)

  • lab4/submission.md — everything graded except the ADR: the two git log verifications (Part A), the pasted Layer Diagram, the Lab 3 Reconciliation, the 3-Tier Comparison, and the module-boundary analysis (Part C). Each has its own heading in the template.
  • lab4/architecture.mmd — the Mermaid diagram source, committed as the working artifact. Only the copy pasted into submission.md is graded.
  • lab4/ADR-001.md — the Architecture Decision Record.
  • lab4/plot-commits.py and lab4/commit-frequency.png — the plotting script and the chart it renders. The script is what gets graded.

Submitting Your Lab

A tag, not the current state of main, is what gets graded.

git add lab4/
git commit -m "Submit Lab 4"
git push origin main
git tag lab4-submission
git push origin lab4-submission

Off campus, connect to the Penn VPN first. To fix something before the deadline, commit the fix, push main, then move the tag:

git tag -d lab4-submission && git push origin :refs/tags/lab4-submission
git tag lab4-submission && git push origin lab4-submission