Week 1 Lesson

What Software Design Really Means

Design is the structure that lets working code survive change.

software designarchitecturemaintainabilityreadabilitychange costtechnical decision

Learning Objectives

  • Explain the difference between coding, software design, and architecture.
  • Identify design problems in code that technically works.
  • Describe why maintainability becomes a real cost in software projects.
  • Use a simple design review checklist on an existing program.

Lecture Notes

A beginner often thinks software is finished when the program runs. A professional learns that running is only the first standard. Real software is read, changed, fixed, extended, tested, deployed, and handed to other people. Software design is the discipline of shaping code so those future actions are possible without turning every change into a fight.

Coding is the act of writing instructions. Design is deciding where those instructions belong, what each part is responsible for, and how the parts communicate. Architecture is the larger structure of the system: major boundaries, deployment choices, data flow, storage, security, and integration points. The three overlap, but they are not the same. A student can write code that produces the correct output while still creating a design that will be painful to modify.

Bad design usually hides at first. A single file can be fast to write for a class exercise. A single class that handles input, validation, database access, calculations, and display can feel convenient. But the pain appears when requirements change. If a professor says, “Now support CSV and JSON,” or a client says, “Add mobile notifications,” a tangled design forces you to edit unrelated parts of the system. This is why software design is about reducing the cost of change.

Design quality is not about showing off patterns or making code look complicated. Good design is boring in the best way. Names reveal intent. Modules have clear responsibilities. Dependencies are visible. Business rules are not buried inside user interface code. Tests can exercise important behavior without launching the whole app. When something breaks, the likely location is easier to find.

One useful design habit is to ask three questions before and after coding: What can change? What should not know about what? What rule must be protected? The first question reveals flexible points. The second reveals boundaries. The third reveals invariants, which are rules that should always stay true, such as “an account balance cannot go below zero” or “a completed order cannot be edited like a draft.”

Worked Example: Messy code vs designed code

Before
// Everything is in one place. This works, but it is hard to change.
function checkout(cart: any[], email: string) {
  let total = 0;
  for (const item of cart) total += item.price * item.qty;
  const tax = total * 0.0825;
  const finalTotal = total + tax;

  console.log('Charging card for ' + finalTotal);
  console.log('Saving order to database');
  console.log('Sending receipt to ' + email);

  return finalTotal;
}
After / Better
type CartItem = { price: number; qty: number };

class PriceCalculator {
  total(cart: CartItem[]): number {
    return cart.reduce((sum, item) => sum + item.price * item.qty, 0);
  }
}

class TaxCalculator {
  constructor(private rate: number) {}
  taxFor(amount: number): number {
    return amount * this.rate;
  }
}

class CheckoutService {
  constructor(
    private prices: PriceCalculator,
    private taxes: TaxCalculator
  ) {}

  checkout(cart: CartItem[]): number {
    const subtotal = this.prices.total(cart);
    return subtotal + this.taxes.taxFor(subtotal);
  }
}
Design lesson: The second version separates pricing from tax calculation and checkout coordination. It is not bigger because bigger is better; it is organized so future changes have obvious homes.

Lab: Bad Code Diagnosis

Scenario: You receive a small program that works but mixes input, calculation, storage, and output in one function.

Steps

  1. Run or read the program and describe what it does.
  2. Highlight every place where two responsibilities are mixed together.
  3. Name at least three changes that would be risky in the current design.
  4. Rewrite the design as a simple module list. Do not refactor yet; just diagnose.

Deliverables

  • One-page design critique
  • List of responsibilities
  • List of likely future changes
  • Proposed module breakdown

Assignment

Pick an app you use often. Write 600–900 words explaining its likely modules, likely data, and three design choices the developers probably had to make.

Discussion Prompts

  • Why is working code not always good software?
  • What is one program you have written that would be hard to change now?
  • When is it acceptable to write quick messy code?

Week 1 Quiz

Use these as quick checks after the lesson.

Question 1

What is software design mainly concerned with?

Question 2

A program with all logic in one function may be okay for a tiny exercise, but becomes risky because:

Question 3

Architecture usually refers to:

Question 4

A good early design question is:

Question 5

Good design should make software:

Student Notes

Saved in this browser only.