Lecture 1: Introduction to Software Development and Design
Overview
This lecture introduces the fundamentals of Software Development and Design, covering Coupling, Cohesion, DRY principles, SOLID principles, and various design patterns. Software design is the critical link between requirements and implementation, helping manage complexity and ensure structure in both small and large projects.
Lecture Material (PDF)
Design Fundamentals
Software design faces several inherent challenges that make it a complex and iterative process. Understanding these challenges helps you approach design problems more effectively.
Wicked Problems
Design problems are often not fully understood until partially solved. Solutions emerge through exploration and experimentation.
Sloppy Process
Design involves trial and error. The final tidy result comes through mistakes and iterations, not perfection on the first attempt.
Nondeterministic
Multiple valid solutions can exist for the same design problem. There's rarely one 'correct' answer.
Trade-offs
Designers must balance competing objectives like performance, simplicity, extensibility, and maintainability based on system requirements.
Emergent Practice
Good design evolves over time through reviews, discussions, and real-world testing. Flexibility to adapt is crucial.
Managing Complexity
The primary technical imperative. Essential complexity stems from real-world requirements; accidental complexity arises from poor design.
Core Principles: Coupling & Cohesion
Two fundamental concepts that determine the quality of your software design. These principles guide how you structure modules and their relationships.
Coupling
The degree of interdependence between software modules. LOW coupling is desirable for flexibility and easier maintenance. Modules should have minimal dependencies on each other.
Cohesion
The degree to which elements within a module belong together. HIGH cohesion means a module performs a single task or closely related tasks, making it easier to understand and maintain.
Loose Coupling
Keep interdependencies between components minimal. Use dependency injection and abstraction to reduce tight coupling between modules.
Design Heuristics
Prioritize minimal complexity, ease of maintenance, and think about future developers who will work with your code.
SOLID Principles
A set of five design principles for object-oriented programming that create more maintainable, flexible, and scalable software systems.
Single Responsibility Principle (SRP)
A class or function should have only one reason to change. It should have only one responsibility, promoting high cohesion and making code easier to maintain, test, and understand.
Open/Closed Principle (OCP)
Software entities should be open for extension but closed for modification. Add new features without modifying existing code, reducing the risk of introducing bugs.
Liskov Substitution Principle (LSP)
Subclasses should be able to replace their parent classes without affecting system behavior. Derived classes must extend base classes properly without altering expected functionality.
Interface Segregation Principle (ISP)
Clients should not be forced to depend on interfaces they don't use. Break large interfaces into smaller, specific ones to promote high cohesion.
Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions. This reduces coupling between modules and makes the system more flexible.
DRY Principle (Don't Repeat Yourself)
Every piece of knowledge or logic must have a single, unambiguous representation in the system. Avoid repetition in code, data, logic, and documentation.
Easier Maintenance
Changing one instance of repeated logic affects all occurrences, reducing the chance for bugs and inconsistencies.
Code Readability
More concise and understandable code because there's no clutter of repeated logic throughout your codebase.
Reduced Complexity
Less code to maintain means fewer places where bugs can hide and easier comprehension of the system.
Implementation Strategies
Refactor repeated logic into functions or classes. Use variables and constants instead of hard-coded values. Extract common patterns into reusable modules.
Software Architecture
Understanding different architectural patterns and layers helps you structure applications effectively for your specific requirements.
Typical Application Layers
Presentation Layer (UI), Business Logic Layer (core logic), Data Access Layer (database interaction), and Database Layer (persistent storage).
Monolithic Architecture
Entire application built as a single unit with tightly coupled layers. Simpler to develop initially but harder to scale and maintain as it grows.
Microservices Architecture
Application divided into small, independent services. More complex initially but easier to scale and maintain specific components independently.
3-Tier Architecture
Common pattern you'll likely use: UI layer (HTML/React), Application layer (Node.js), Database layer (SQL). Clear separation of concerns.
Design Levels
System Level (organizing into subsystems), Class Level (dividing into classes), and Routine Level (designing individual methods).
Good Software Characteristics
Reusability, extensibility, high fan-in (maximizing use of utility classes), and portability across platforms.
Tools & Further Reading
Additional resources to deepen your understanding of software design principles and best practices.
VLE Page
Course management and resources.

Design Principles and Design Patterns
Additional reading on software design.

Coupling and Cohesion Metrics
Research paper on measuring coupling and cohesion.