All Files in ‘SENG301 (2021-S1)’ Merged

01. Introduction

Course breakdown:

Preparation/reading required before each lecture.

https://agilemanifesto.org

Core Values

Principles

Our highest priority is to satisfy the customer through early and continuous delivery of valuable software.

Welcome changing requirements, even late in development. Agile processes harness change for the customer’s competitive advantage,

Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale.

Business people and developers must work together daily throughout the project.

Build projects around motivated individuals. Give them the environment and support they need, and trust them to get the job done.

The most efficient and effective method of conveying information to and within a development team is face-to-face conversation.

Working software is the primary measure of progress.

Agile processes promote sustainable development. The sponsors, developers, and users should be able to maintain a constant pace indefinitely.

Continuous attention to technical excellence and good design enhances agility.

Simplicity - the art of maximizing the amount of work not done - is essential.

The best architectures, requirements, and designs emerge from self-organizing teams.

At regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly.

Scrum

Scrum empowers the team with a large amount of freedom to ensure the team can meet their goals.

Both individuals and teams should be able to learn and improve, and mechanisms should be in place to ensure knowledge/learning can be transferred across individuals/teams.

Lean

See https://roadmunk.com/guides/lean-development/ and [https://agilevelocity.com/7-principles-of-lean-software-development/](Agile Velocity).

Optimization process based on Toyota car manufacturing in the 1950s.

Principles

Kanban

See https://kanbanize.com/kanban-resources/getting-started/what-is-kanban.

In summary, Kanban is layer on top of (but not replacing) existing processes that encourages incremental changes and decision making at all levels.

Kanban limits the number of work in progress items in each stage to reduce the amount of wasteful context switching and avoid clogging up the pipeline further up.

Metrics:

02. Scrum 101

Scrum Values

Lifecycle

Sprints center around the backlog; user stories and functionality that needs to be completed:

Ceremonies

Initial Startup

Compared to kanban which is task-oriented, scrum has a larger initial phase before the first sprint.

Sprint Planning

Chunking stories into tasks may help with prioritization

Each implementation task should be sufficiently described: mockups, design, acceptance criteria etc.

User Stories

A promise of a conversation to be had:

Part 1: The What

Planning Poker

Uses Fibonacci-like progression (0.5, 1, 2, 3, 5, 8, 13, 20, 40, 100, ∞, ☕, ?); reflects increasing uncertainty in estimates.

Play the hands, then discuss why each person chose the value. Repeat until a consensus is reached.

For the first time the group is together, come up with a hypothetical scenario to calibrate what each number means.

NB: Don’t try and convert it to hours

NB: for large numbers, using the exact Fibonacci values makes the estimate seem more accurate than it really is.

Part 2: The How

For the first few sprints, make an estimate and then double it; delays will cascade and affect dependent tasks.

Monitoring

Standups

Every morning (if full time: 2/week for SENG302), answer three questions:

Review

Retrospective

The team discusses what happened in the sprint.

Discuss issues about:

Ask what are we doing well? How can we fix improve it?

Return to the next retrospective asking if improvements were made.

Bubble method: create a list of issues alone; pair with another team member an discuss. Repeat until the whole group is together.

Circle method: create a list of action items, sorting them by how well they went. Group close and related items together and fix these as a whole.

Lessons from the Tutorial

What is Ready and Done?

Ensure all team members have the same understanding of these two words. What quality level is expected for ‘ready’ or ‘done’?

For stories, ready could mean:

Done could mean:

03. Agile Requirements Analysis

“The best way to get a messed up project is to start earlier than the basic requirements have been defined” - Mario Fusco.

Scrum

Roles

Product owner:

Scrum master:

Team:

Product Backlog Items

Epic:

Story:

Task:

Product vs Sprint Backlog

Product backlog:

Sprint backlog:

Scrum Board

All items should be in one of the following columns:

Snow ploughing:

Users

Personas

Can’t think in terms of some generic user:

Personas:

Keep the number of personas limited.

User Profiles (Actors)

Focusing on classes of users. Classes can be defined by the users’:

Comparison

Profiles and personas should be defined by user interviews, not imagination.

Personas focus more on what their motivations while profiles focus more on who they are

User Stories

Usually follows the template: As a role, I action so that value.

The story is a promise of the conversation.

INVEST

Ask if you can break the story down further and still get value from them. If so, break it down.

Promote the story into an epic if:

Validating Requirements

Common practices:

Slicing Tasks

Don’t have people dedicated to a particular layer of the project (e.g. frontend, backend, database):

Instead, tasks should involve the whole stack:

SMART

SMART tasks are:

04. Agile Team Management

Servant Leadership

The scrum master should help, not direct, their team:

Archetype of a Scrum Master

The scrum master should empower communication:

They also need to protect the team when necessary; act as a shield to ensure the team can be successful.

They should refrain from jumping into technical details:

Team Organization

Agenda

Sprint planning:

Create an agenda:

Changes

The sprint plan should be protected: decisions were made with the PO and should not be changed on a whim.

Stakeholders may come at any time and ask for:

The developer must know the threshold between the two and communicate with the stakeholders:

Issues and Bugs

Issues: problem identified before a review at the latest.

Bug: problem discovered at the earliest during later regression tests; these must be added to the backlog.

High priority bugs may be taken into account during the current sprint. This requires discussion with the PO (as items are being re-prioritized).

If direct communication is used instead of formal reports, ensure reproduction steps are communicated.

Impediments

Record traces of all issues for sprint reviews and retrospectives so that the team can learn from it if you face the issue again. See [https://xkcd.com/979/](Wisdom of the Ancients)

Bad Behavior Patterns

Reactive behaviors:

05. Monitor Project Progress

How do you track project progress? Key Performance Indicator. KPIs are:

These could be things such as system uptime, income, returning visitors.

Metrics:

Every Agile framework has its own set of metrics. Lean measures execution time; kanban on task flow; scrum on the team’s ability to deliver.

Monitoring Code Quality

Pair Programming

Pair programming is a great way to ensure high code quality and monitor task progression: the code you write is a reflection of the team dynamics.

Code Review

Review the code for:

Sprint Reviews

The very last moment where you can track progress. Ask the PO before the review if necessary.

Semi-Automated Tools

Automated checks:

Aggregated vs simple metrics:

Simple metrics for targeted aspects:

Refactoring and Re-engineering

Refactoring increases code quality:

Re-engineering fixes behavioral issues:

Team Dynamics

Communication:

Stand-ups:

Retrospectives:

Sprints and Releases

Burn-down charts: graph mapping days remaining until end of sprint against remaining story points (or possibly hours of work left).

Alternative release burn-down chart:

Sprint interference chart:

Remedial focus chart:

06. Agile Software Modelling

System architecture is a reflection of organization hierarchy.

Modelling

Used to handle complexity: lots of functionality, interactions (possible concurrent) and constraints.

Identifying components: reuse existing libraries/resources.

Abstraction levels have been raised over the years to try reduce complexity?

Representations/models/visuals are good if:

Visual representations should:

Representations are a model of reality. All models are wrong; some models are useful.

Class Diagrams

Every organization has their own business rules, their own vocabulary, and concepts (with relations to each other).

Domain concepts:

Class diagrams are a static representation (not business logic). The semantics of all elements must be clear and fully unambiguous.

The terminology used by the client should also be used by in the code.

Tactics

Architecture tactics:

Architecture style and design patterns:

These shape the system design early on:

Documentation

README files should state:

Wiki pages:

Stories as Use-Case Scenarios

From a story it is difficult to:

Robustness diagrams:

  웃 ------ |-o  -----> ⥀ ----- o̲ ◆-----> o
actor    boundary   control   entity  property

OR:

Example: As a user, I want to search for events by their types, location or date so that I will be able to subscribe to them later.

user    event interface  subscribe
  웃 -------- o-| ------->  ⥀
              |             |
              |             |
              v           event
      search  ⥀ ----------  o ◆--- o location
                            ◆ ◆
                            |   \
                           date  \
                                  type

Partial scenarios

As a user, I want to do x so that value: the value does not need to be implemented in the story, the story acts as justification for the task.

Wireframes

c.f. robustness diagrams:

07. Testing and Mocking

While designing Prepare
Requirements Acceptance tests
System requirements System tests
Global design Integration tests
Detailed design Unit tests

Objectives

Testing shows the presence of bugs, not their absences.

Two main objectives:

Determine if it is fit for purpose:

Staged Testing

Run unit tests, then component tests, then system tests (scenario-based user testing).

Unit Testing

Methods should also prevent missuses:

Be skeptical:

Component Testing

Various types of interfaces:

Be skeptical of input data:

System Testing

Scenario-based testing:

Trace and record test executions in some structured way:

Agile Testing

For each commit run:

Peer reviews should also be done before merging.

Any successful builds should become a release candidate.

Run manual tests on candidate at end of each sprint.

Run automated performance tests before each release.

Assertion clauses:

Guideline-based testing identifies common programming mistakes (e.g. null values) and ensures tests are performed on these aspects.

Acceptance Test-Driven Development

User stories accompanied by acceptance criteria.

To automate the tests:

Automated Acceptance Tests

Use the same path as the users; playback tools like Selenium test directly on the GUI, but may be fragile and time-consuming.

Hence, decoupling the UI from business logic is useful.

Acceptance criteria can be tested using Cucumber.

Sprint Reviews

Prepare and plan sprint reviews:

Capacity, Load and Stress Testing

Quality requirements are often difficult to test (e.g. maintainability, auditability are not testable).

Capacity-focused requirements can be tested in a semi-automated fashion: response time can be expressed as user stories or as required system features.

08. Reliability, Resilience and Security

Ethics

Equality: same treatment for everybody

Equity: customized treatment to ensure everyone has the same opportunity

Algorithms and AI: garbage in, garbage out. If the dataset it is fed is biased, the output will be biased.

ACM code of ethics. TL;DR: respect everyone + make mistakes and reflect on your mistakes.

Reliability and Resilience

Faults, errors and failures:

To improve reliability:

Availability and reliability:

Works-as-designed problem:

Reliability can be subjective, affecting only a subset of users:

Capacity management:

Architectural strategies:

Reliability Guidelines

Unicorns:

Security treats come from:

The Four Rs of a Resilience Engineering Plan

Resilience planning:

Checklist:

09. Continuous Integration

Good programmers write code that humans can understand

The Integration Problem

When combining units into a product (after testing each unit individually), problems occur with integration:

Diagnosis gets harder as more units are combined.

Continuous Integration

Working software is the primary measure of progress

Agile Manifesto, 7th Principle

Integrate as you develop; when a story or unit is ready, integrate it immediately with master.

The smaller a chunk of code is, the easier it is to test and the easier it is to integrate.

Fowler’s Principles

  1. Single source repository
  2. Automate the build process
  3. Make the build self-testing
  4. Everyone commits to master/main every day
  5. Every commit trigger a build on an integration machine
  6. Broken builds should be fixed immediately
  7. Keep the build fast
  8. Test in a clone of the production environment
  9. Make it easy for everyone to get the latest executable
  10. Ensure everyone can see what is happening
  11. Automate the deployment phase

Single Source Repository

All code and resources should be in a single place:

Version Control:

Driessen’s Branching and Merging Strategy

Everyday Commits

Merge Requests and Code Review

Ensure:

Merge requests increases cross-functional knowledge and helps with onboarding.

When leaving feedback, be open-minded:

As a reviewee, remember that comments are made against the code, not you.

Be constructive and positive in giving feedback; point out what they have done right.

Managing Builds

Build after every commit. To reduce the burden of manual configuration, create automation scripts and use an integration server accessible to the team.

Self-testing: automate the build with upfront tests.

To make builds faster:

Make a snapshot of the latest stable build accessible to the team.

Automated Deployments

Requires the deployment to be scripted.

Requires rollback scripts too:

10. Continuous Delivery, Deployment and DevOps

The Deployment Problem

The ‘It works on my machine’ problem.

Deployment Antipatterns

Doing it manually:

Waiting until the very end to deploy a release:

More Releases

Increase release frequency: you will either suffer more or find a way to make it easy.

Continuous Integration:

Benefits of Continuous Delivery:

Deployment Strategies

Adequate preparation.

You need to:

Automate as much as possible:

Create a disaster recovery process (e.g. rolling back DB to previous schema)

Configuration Management

Keep everything under version control:

Think about dependencies: automated dependency retrieval is handy, but try to avoid having to ‘download the internet’.

Deployment Pipeline

After each stage, review the results/metadata from the pipeline.

Store the last few binaries in the artefact repository so that you can easily rollback.

Practices

Build only once:

Creating Deployment Strategies

Zero Downtime Releases

Negate as much downtime as possible (or at least, during peak times).

Hotplug facilities exist:

If not possible:

Blue-Green Deployment

In distributed systems e.g. web apps, re-routing is easy and it is possible to have multiple versions available at the same time.

Have two identical environments, blue and green for each server type (e.g. web server, application server, database server), using a router to determine which slice is being used at any given time.

Canary Deployment

Real-world systems are not easily cloneable:

Hence, use canary deployment development:

11. Wireframes, Mockups, Prototypes and User Experience

Basic Principles

Usability

User experience is more than usability:

Have empathy:

Design makes a difference; two identically-capable websites will be distinguished by their UI

User Experience Honeycomb

Useful: is your interface serving a purpose?

Usable: is it easy to learn and use? Is the learning curve short?

Findable: can users find what they are searching for?

Credible: is the company and product trustworthy?

Accessible: is it accessible to disabled people? Aria labels, contrast, font size etc.

Desirable: does the UI look good and make it easy to use?

If it has all six, then your application has value.

What Makes a Great UX?

Not Just Style Sheets

Simplicity: do not overload the UI; white space is important

Consistency: consistent behavior (e.g. button and links have consistent behavior) and styling (colors, page layout, button names)

Dual coding: use icons and labels; just icons makes it accessible only to expert users

Don’t annoy the user: avoid video autoplay, pop-ups

Content

Appropriate content:

Keep users informed:

UX Tools

Input controls:

Navigational components: Design navigational components for your purpose; don’t just plonk it on there

If a user doesn’t understand how the navigation works, they will just leave

Information components:

Plus containers - accordions, tables, tabs etc.

Infographics

Semiology of Graphics

Visual Variables

Misc:

Test Test Test

Wireframes: low-fidelity sketch; flow between screens. Be extra careful who you engage with; if you show the wireframes, everyone and their dog will have different nitpicks and complaints.

Mock-ups: high-fidelity representations based off of the wireframes; no interactivity. Focus on the visual identify.

Prototype: limited functional implementation (e.g. mocking) but with a functioning workflow. Concentrate on visual behavior, risky features.

12. Design Principles 1

How do we quantify ‘good’? How precise can we be? Common language required to describe this.

Big-picture language

Cohesion:

Coupling:

Cohesion good; coupling bad.

Why is software design hard?

Problem: complexity

Solution: decomposition

Methodologies

Top-down:

Bottom-up:

Nucleus-centred (OO):

Aspect-oriented (e.g. security):

Nucleus-Centred Design

Start with the tricky bits.

Hide information; decisions, choices, things that may change in the future (David Parnas).

Identify decision decisions with competing solutions; isolate these details behind an interface i.e. OO-design; state/access methods encapsulated.

Example: Student

Student             StudentDialog
name      <-------- student
id
display()?          display()?

Where should display() go?

If in StudentDialog, allows for separation of concerns.

If in Student, keeps related data and behavior together, increasing cohesion.

Information-Hiding

Encapsulation: drawing a boundary around something.

Information hiding: hiding design decisions from the rest of the system to prevent unintended coupling.

Encapsulation = programming feature; information hiding = design pattern.

Encapsulation Leak

When implementation details get exposed, allowing implementation details to be exposed and private properties to be modified outside of the class, causing inconsistent/invalid states.

// Student
private Set<Enrolment> enrolments;
public Set<Enrolment> getEnrolments() {
  // Bad
  return enrolments;

  // Better: items cannot be added/removed, but setters/getters can still be called on elements
  // Deep clone?
  return Collections.unmodifiableSet(enrolments);
}

// Course
add(Student student) {
  // Bad: set is being modified outside of the Student class
  student.getEnrolements().add(new Enrolment(student));
}
Coping with Change

Figure out the solid bits; the invariants, and make them the framework of your program. Hopefully, the problem won’t change.

Find the wobbly bits and hide them away. In other words, make stable abstractions.

Hiding Design Decisions

If you chose something (i.e. multiple options available), hide it:

A DAO (data-access objects) allows us to define an interface to access and modify objects.

This allows the internal implementation details to be hidden and thus changed as needed.

Open-Closed Principle

Make your system open for extension but closed for modification.

Any new use case should be able to add to the interface without requiring it to be modified.

Tell, Don’t Ask

Decisions based entirely upon the state of one object should be made inside the object itself.

Avoid asking for information from an object and using it to make decisions about that object.

If this is happening, it may be a sign that the data is being stored in the wrong place.

Example:

m = new VendingMachine();
m.vend(3); // vending machine should check and restock itself
if (m.stock < 10) m.reorder(); // asking: bad

13. Design Principles 2

Keep it simple:

Leave decisions to the last responsible moment: delay the decision so that you have the most information.

Refactoring

Leave the world a better place than you found it.

Martin Fowler abridged: refactoring makes small behavior-preserving transformations, each of which are ‘too small to be worth doing’ but cumulatively have a significant effect.

Object-Oriented Design

Data modelling: focus on data internal to the object

Behavior modelling: focus on services provided to the external world

OO models both; just a question of where you start from.

Inheritance: The Dark Side

Mistakes:

Principle: if it can change, it isn’t inheritance.

Using Inheritance for Implementation

Favour composition over inheritance.

e.g. instead of stack inheriting from vector, stack should have the stack as a private variable; the data store for the stack.

‘Is a-role-of’

Example inheritance tree:

             Person
   Student              Staff
   Postgrad       Tutor  Admin  Lecturer
                                Professor

What if a person is both a postgrad and tutor?

Instead, have each Person have multiple Roles. In the real world, when a person changes role, the person doesn’t change.

This allows for separation of concerns.

The ‘Becomes’ Problem

e.g. EligibleStudent and IneligibleStudent inherit from Student. What happens if a student becomes ineligible: the object must switch class becomes of a relatively trivial detail.

Just have eligibility as a boolean in the Student class.

Principle: Inheritance isn’t dynamic.

Over-Specialization

Method arguments etc. should use the most general interface/class you can get away with.

Violating the Liskov Substitution Principle

LSP: the behavior of a method shouldn’t change regardless of the subclass of arguments it is given.

e.g. setWidth, setHeight method for a Rectangle. Square is a subclass of Rectangle. Method taking Rectangle as argument could be given a Square: if it sets the width, the height will be set silently; area/perimeter calculations will give unexpected results.

If methods start telling white lies, you start walking along the path to hell.

Single Responsibility Principle

Every module or class should have responsibility over only one part of the functionality - this responsibility should be fully encapsulated.

Why?

Typically found in controllers, initializers.

Interface Segregation Principle

No client should be forced to depend on methods/interfaces it doesn’t use.

e.g. iPhone interface can be split into widescreen iPod, phone, internet communicator device interfaces.

Dependency Inversion Principle

1: When high-level modules depend on low-level modules; both should depend on abstractions.

2: Abstractions should not depend on details; details should depend on abstractions.

i.e. high-level objects should not depend on low-level implementations. Use abstractions; no one likes micro-managers.

When things change, you want as little stuff around it to change.

To do this, try to avoid new; explicitly instantiating concrete instances.

Dependency Injection

Passing objects (that conforms to the broadest interface you can get away with) through a constructor rather than creating a concrete instance of it yourself.

SOLID Principles

Single responsibility.

Open-closed principle.

Liskov substitution principle.

Interface segregation principle.

Dependency inversion.

14-19. Design Patterns

Object-Oriented-Design Experience

High-level: structural models (layered, client-server etc.), control models, pipes & filters.

Idioms: getters/setters, while(&dst++ = *src++) etc.

What comes in between? OOD wisdom - collections, event loops/callbacks, MVC etc.

Sidenote: Literate Programming: Tangle & Weave

The order in which code makes sense to read is not the same the compiler wants to receive - so weave up the documentation and tangle up the source code.

Talk about different parts of the program from different directions - some things are best described as code, some as text, some as diagrams.

What are Design Patterns

The core is a simple class diagram with extensive explanation - documentation for an elegant, widely-accepted way of solving a common OO design problem.

Design patterns are:

A definition for ‘Design Pattern’

A solution to a problem in a context

Forces

Correctness: completeness, type-safety, fault-tolerance, security, transactionality, thread safety, robustness, validity, verification etc.

Resources: efficiency, space, ‘on-demand-ness’, fairness, equilibrium, stability etc.

Structure: modularity, encapsulation, coupling, independence, extensibility, reusability, context-dependence, interoperability etc.

Construction: understandability, minimality, simplicity, elegance, error-proneness, etc.

Usage: ethics, adaptability, human factors, aesthetics etc.

Resolution of Forces

Impossible to prove a solution is optional; make argument backed up with:

Documenting Patterns

Documenting Pattern Instances

The Gang of Four

The design patterns book authored by Gamma, Helm, Johnson and Vlissides containing a catalog of 23 design patterns, each being a creational, structural or behavioral pattern.

Creational Patterns

Abstract Factory (AKA Kit)

Interface for creating families of related/dependent objects without specifying their concrete classes.

e.g. when making UI elements, want scrollbars, buttons etc. to all use the same theme.

public abstract AbstractFactory {
  public AbstractProductA createProductA();
  public AbstractProductB createProductB();
}

public class ConcreteFactory1 extends AbstractFactory {
  public AbstractProductA createProductA() {
    return new ConcreteProduct1A();
  }
  ...
}

Builder

Separates construction of a complex object from its representation so that the same construction process can create different representations.

Factory Method (AKA Virtual Constructor)

Define interface for creating an object and let subclasses decide which concrete class to instantiate - allows instantiation to be deferred to its subclasses.

Problem: code that expects an object of a particular class doesn’t need to know which subclass the object belongs to (as long as it follows the LSP).

The exception to this rule is when creating a new object - you must know its exact class. Hence, ‘new’ is glue.

Broken polymorphism:

if (isWizard())       weapon = new Wand()
else if (isFighter()) weapon = new Sword();

Solution:

public abstract Creator {
  public abstract Product factoryMethod();
}

public class ConcreteCreator implements Creator {
  public Product factoryMethod() {
    return new ConcreteProduct();
  }
}
Creator
factoryMethod()
doSomething()
      △ 
      |
      |
ConcreteCreator
factoryMethod() 

Note that it is common to have more than one factory method.

Parameterized factory methods can produce more than one type of product, add constraints/details etc.

public makeWeapon(Type type) {
  if (type == Weapons.DAGGER) return new Dagger();
  ...
}

public makeWeapon(Owner self) {
  if (self.height() > 180) return BigDagger();
  ...
}

Prototype

Specify the kinds of objects to create using a prototypical instance, creating new objects by copying the prototype.

Singleton

Intent: ensure class only has one instance and provide a global point of access to it.

Problem:

Solution:

<<singleton>>
Singleton
private $uniqueInstance

public  $getInstance()
private Singleton()

(NB: $ means static)

Use lazy initialization approach in the getInstance method.

Issues:

Structural Patterns

Adapter

Converts interface of a class into an interface the client expects - allows classes with incompatible interfaces to work together.

Bridge

Decouple an abstraction from its implementation so they can be varied independently of each other.

Composite

Problem: objects contain other objects to form a tree, but want client code to be able to treat composite and atomic objects uniformly.

e.g. Person has eat(FoodItem) method where Bread etc. is a FoodItem. Meal is composed of multiple FoodItems so a new method, eat(Meal) is required.

Solution: create abstract superclass that represents both composite and atomic objects.

Used in Swing’s JComponent.

public class Client {
  private Component component;
}

public abstract Component {
  public doSomething();

  /*
  These methods are sometimes defined in Composite
  */
  public add(Component);
  public remove(Component);
  public getChild(int);
}

public class Leaf {
  public doSomething();
  public add(Component component) {
    throw new NotImplementedException();
    // Or: 
    return false;
  }
  ...
}

abstract class Composite extends Component {
  private List<Component> components;

  public doSomething() {
    for(Component component: components) {
      component.doSomething();
    }
  }

  public add(Component component) {
    components.add(component);
  }

  public remove(Component component) {
    components.remove(component);
  }

  public getChild(int index) {
    return component.get(index);
  }
}

Notes:

Decorator

Attaching additional responsibilities to an object dynamically - an alternative to subclassing for extending functionality. It allows you to extend the existing functionality but not add new public methods.

You can only have one subclass at a time (C++'s multiple inheritance leads to hell) but you can have multiple decorators at the same time: composition over inheritance.

Solution: use aggregation instead of subclassing.

abstract Component {
  public void doSomething();
}

public class ConcreteComponent extends Component {
  public void doSomething() {
    ...
  }
}

public class Decorator extends Component {
  protected Component component;

  public void doSomething() {
    component.doSomething();
  }
}

public class ConcreteDecoratorA extends Decorator {
  public void doSomething() {
    super.doSomething();
    addedBehavior();
  }

  private void addedBehavior();
}

public class ConcreteDecoratorB extends Decorator {
  private State addedState;
  public void doSomething() {
    super.doSomething();
  }
}

Example: Swing’s JScrollPane can be attached to any pane.

Notes:

Façade

Providing an unified interface to a set of interfaces in a subsystem - a higher-level interface to make the subsystem easier to use.

Flyweight

Using sharing to support large numbers of fine-grained objects efficiently.

Proxy

Providing a surrogate/placeholder for another object to control access to it.

Behavioral Patterns

Chain of Responsibility

Avoid coupling the sender of a request to its receiver by allowing more than one object to handle the request - chain receiving objects and pass the request along the chain until an object handles it. (e.g. errors bubble up until it gets handled).

Command (AKA Action, Transaction)

Intent: encapsulate the request as an object to

Participants:

// e.g. remote control
public class Invoker {
  private Command command;
  constructor(Command command) {
    // e.g. light toggle command
    this.command = command;
    command.execute();
  }
}
}
public abstract Command {
  void execute();
  void unexecute();
}

public ConcreteCommand implements Command {
  private Receiver receiver;
  public ConcreteCommand(receiver) {
    this.receiver = receiver;
  }

  public void execute() {
    receiver.action();
  };

  public void unexecute() {};
}

public class Receiver() {
  // e.g. smart light switch
  public void action();
}

public class Client {
  private Receiver receiver;
  private ConcreteCommand command;
  Client() {
    receiver = new Receiver();
    command = new ConcreteCommand(receiver);
  }
}

From reading Refactoring Guru’s Command pattern page:

// Receiver: where the business logic lives
class Data {
}

// Client: configures commands, passes them to invokers
class App {
  constructor() {
    this._data = new Data();
    this._history = [];
    this._stuffCommand = new deleteCommand(this._data);
    this._stuffButton = new Button("Delete", () => this._execute(this._stuffCommand))
    document.body.appendChild(this._stuffButton);
  }

  _execute(command) {
    command.execute();
    this._history.push(command);
  }

  _unexecute() {
    if (this._history.length) this._history.pop().unexecute();
  }
}

// ConcreteCommand: command which calls business logic
class StuffCommand {
  constructor(data) {
    // Gets data in the constructor or on its own
    this._data = data;
    this._rand = Math.random();
  }

  execute() {
    // Put command in queue, etc.
    this._snapshot = this.data.snapshot();
    this._data.doStuff(this._rand);
  }

  unexecute() {
    // Generic so can be done in app, but more efficient method will need to know what changed
    // and how to undo it, so must be within the command
    this._data.restore(this._snapshot);
  }
}

// Invoker
class Button {
  constructor(text, command) {
    this.text = text;
    this.command = command;
    this._button = document.createElement("button");
    this._button.addEventListener("click", () => this.command.execute());
  }
}

Interpreter

Given a language, define a representation for its grammar and an interpreter that uses this to interpret sentences.

Iterator (AKA Cursor)

Problem:

Solution:

public interface Collection {
  public Iterator createIterator();
}

public interface Iterator {
  public Element first();
  public Element next();
  public boolean isDone();
  public Element currentItem();
}


public class ConcreteCollection implements Collection {
  public ConcreteIterator createIterator() {
  }
}

public ConcreteIterator implements Iterator {
  ...
}
<<interface>>                 <<interface>>
Collection                   Iterator
createIterator()   ------->  first()
      △                      next()
      |                      isDone()
      |                      currentItem()
      |                          △
      |                          |
ConcreteCollection           ConcreteIterator

The set iterator in Java does not have a first method as there is no guaranteed ordering.

for(Collectable c: someCollection) {

}

// Implicitly does:
Collectable c;
Iterator<Collectable> iterator = someCollection.iterator();

while(iterator.hasNext()) {
  c = iterator.next();
  // But the explicit version can also do
  if (c.val == 10) {
    iterator.remove();
  }
}

Mediator

Define an object that encapsulates how a set of objects interact with each other - reduces coupling by keeping objects from referring to each other explicitly.

Memento

Capture and externalize an object’s internal state (without violating encapsulation) so that it can be restored to this state.

Observer (AKA Publish-Subscribe, Dependents)

One-to-many dependency between objects so that all dependents are notified when an object changes state.

Problem: separate concerns into different classes while avoiding tight coupling and keeping them in sync (e.g. separating GUI code from model).

Solution:

Subject                        Observer
attach(Observer)          0..*  
detach(Observer)  -----------> update(Observable, Object)
notify()
   △                                  △
   |                                  |
   |             1                    |
ConcreteSubject  <------------ ConcreteObserver
doSomething()    subject       update()

doSomething will call notify

In Java:

JavaFX introduced java.beans.PropertyChangeSupport:

private pcs = new PropertyChangeSupport(this);

addListener(PropertyChangeListener)
removeListener(PropertyChangeListener)
pcs.firePropertyChange(String, oldVal, newVal)

class Observable {
  private PropertyChangeSupport pcs;
  public Observable() {
    pcs = new PropertyChangeSupport(this);
  }

  addListener(PropertyChangeListener pcl) {
    pcs.addPropertyChangeListener(pcl);
  }

  removeListener(PropertyChangeListener pcl) {
    pcs.removePropertyChangeListener(pcl);
  }

  doSomething() {
    pcs.firePropertyChange(name, oldVal, newVal);
  }
}

class Observer implements PropertyChangeListener {
  public propertyChange(PropertyChangeEvent pce) {

  }
}

Notes:

State

Intent: allow objects to alter behavior when their internal state changes - the object appears to have changed class.

TL;DR treat objects as FSMs that change their behavior depending on state.

Need a state chart representation of object behavior e.g.

            admin approved
o draft <------------------- 
    ^         expired       |
    | submit/               v
    | review failed      published
    v                       ^
  review ___________________| 
          admin reviewed

When a new stage gets added, what happens to existing object?

The pattern:

Implementation:

public abstract State {
  void handle1();
  void handle2();
}

public class ConcreteStateA extends State {
  private Context context;

  // May possibly be in the abstract class
  public void setContext(Context context) {
    this.context = context;
  }
  public void handle1() {
    State state = new ConcreteStateB();
    context.changeState(state);
  }
  public void handle2() { }
}

public class ConcreteStateB extends State {
  public void handle1() { }
  public void handle2() { }
}

public class Context {
  State state;
  public Context(State initialState) {
    state = initialState;
  }

  public void changeState(State state) {
    this.state = state;
  }

  public void request1() {
    ...
    state.handle1();
  };

  public void request2() {
    ...
    state.handle2();
  }
}

Strategy (AKA Policy)

Intent: define a family of algorithms, encapsulating each one and making them interchangeable - Strategy lets the algorithm vary independently from the client that uses it.

Problem: change an object’s algorithm dynamically, rather than through inheritance.

Solution: move the algorithms into their own class hierarchy (composition over inheritance).

Used by AWT/Swing layout managers.

public abstract Strategy {
  protected Context context;

  public Result algorithm();
}

public class ConcreteStrategyA extends Strategy {
  public Result algorithm() {
    ...
  }
}

...

Notes:

Template Method

Define the skeleton of an algorithm, deferring some steps to subclasses - allows subclasses to redefine certain steps of an algorithm without changing its structure.

Problem: implement the algorithm skeleton but not the details

Solution: put the skeleton in an abstract superclass and use subclass operations to provide the details

public abstract AbstractClass {
  public final void templateMethod() {

  }

  abstract protected void primitiveOperation1();
  abstract protected void primitiveOperation2();
}

public class ConcreteClass extends AbstractClass {
  protected void primitiveOperation1() { }
  protected void primitiveOperation2() { }
}

Hooks:

e.g. PrepareMeal abstract class may have isTakeAway hook. Subclass could call it during their assemble to change how the meal is assembled depending on if it is an eat-in or takeaway meal.

Visitor

Represent an operation to be performed on elements of an object structure - allows new operations to be defined without changing the classes of the elements it operates on.

Pattern Language

Alexandrian Patterns

Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such as way that you can use this solution a million times over, without ever doing it the same way twice

Christopher Alexander, A Pattern Language

A set of interrelated patterns all sharing some of the same context and perhaps classified into categories.

Abstract Factory meets Singleton

class ConcreteFactory extends AbstractFactory {
  private ConcreteFactory instance;
  public static getInstance() {
    if (instance == null) instance = new ConcreteFactory();
    return instance;
  }

  public ConcreteProductA getA() {
    return new ConcreteProductA();
  }
}

Iterator meets Factory Method

interface AbstractCollection {
  /**
   * This method is a factory method that returns a product
   */
  Iterator iterator();
}

interface Iterator { ... }

public class ConcreteCollection implements AbstractCollection {
  ConcreteIterator iterator();
}

public class ConcreteIterator implements Iterator { ... }

20. Design by Contract™

Preconditions: before calling service, client must check everything is ready.

Invariants: something true both before and after the service is called

Postconditions: promises the service makes that should be met after the service finishes

e.g.

public class Stack<T> {
  /**
   * Precondition: stack not empty
   * Postcondition: stack unchanged, last pushed object returned
   */
  public <T> peek();

  /**
   * Precondition: stack not empty
   * Postcondition: stack size decreased by 1, last pushed object returned
   */
  public <T> pop();

  /**
   * Precondition: stack not full
   * Postcondition: stack sized increased by 1, `peek() == o`
   */
  public void push(T o);
}

The contract for a class is the union of the contracts of its methods.

Testing

Contracts inform testing; assertions allow us to check:

        Preconditions/
         Input Values
              |
              v
    |--------------------|
    | Software Component |
    |                    |
    | Errors/Exceptions  | --> Side Effects
    |                    |
    |--------------------|
              |
              v
       Postconditions/
        Output Values

Inheritance

Subclasses may have different pre/post conditions. Would require checking if the object is an instance of the subclass to determine if preconditions are met - this breaks the LSP.

Contracts are inherited:

That is, require no more, promise no less.

Hence, instead of saying that ‘Bar is-a Foo’, we can more formally say 'Bar conforms to the contract of Foo.

Guidelines

Specifying Contracts

Eiffel:

Informally, Java has assert expression: message; statements that are disabled by default (-ea flag required )

Philosophy for Exceptions

Java Exceptions iff a contract violation occurs.

Handling violations:

Hence, exceptions must be caught anywhere where clean up is needed.

Interfaces are contracts

Inheritance: The Dark Side

21-22. Code Smells

Process

  1. Sniff
  2. Prioritise and evaluate
  3. Refactor:
    • Split
    • Join
    • Move
    • Extract
    • Rename
    • etc.

How large is large?

Morphology

Fan-out:

Fan-in:

Method Length

Metrics:

Long methods a problem because:

How long is too long? Use:

Object-Oriented Metrics

Chidamber & Kemerer suite commonly used:

Large Class Smell

Knows too much, does too much; violating single responsibility principle

i.e. a God class

Break into smaller cohesive classes: extract class, extract interface

Long Parameter Lists

Solutions:

Duplicated Code

Want a single place of truth.

Solution: extract it into a method

Message Chains

bla.getSubProperty().getSubProperty()...

Bad because:

Law of Demeter: only talk to immediate friends.

Method m() of object o should only invoke methods of:

Dead/Unreachable/Deactivated/Commented Code

Dead code:

Unreachable code:

Deactivated code:

Commented code:

Switch Statements

Large switch/if statements:

Solutions:

Can leave it alone if it just performs some simple actions

Comments

Solution:

Names

Type Embedded in Name

Found mostly in old code. e.g. strFirstName

What happens if type is changed? Decisions should be hidden.

Uncommunicative Names

Should be descriptive, succinct, and have consistent names.

Speculative Generality

When you make general solutions because you speculate/anticipate what you might need in the future: do not speculate about tomorrow’s problems.

YAGNI, so don’t over-engineer your solution.

But at the same time, need to balance this with planning or extensibility.

Solutions:

Inappropriate Intimacy

How much does a class need to know about another? Ideally little; low coupling is preferable.

Solutions:

Indecent Exposure

Feature Envy

Method making extensive use of another class (e.g. envious of their methods and which they had them).

Cohesive elements should be in the same module/class.

Shotgun Surgery

When making a change requires splattering lots of small changes across a large swath of the system; changes should be localized.

This probably means the single responsibility principal has been violated.

Solutions:

Test Smells

Hard to test code:

Obscure tests:

Production bugs:

High maintenance:

Fragile tests:

Erratic tests:

SENG301 Exam Notes (01-13)

Methodologies

Waterfall: Build it Twice

Spiral

Agile

Core Values

Principles

  1. Satisfy the customer: early and continuous delivery
  2. Embrace changing requirements
  3. Deliver working software frequently
  4. Business + developers work together
  5. Trust and support motivated individuals
  6. Face-to-face is the best communication medium
  7. Working software is the primary measure of progress
  8. Sustainable development; a marathon, not a sprint
  9. Continuous attention to technical excellence and design
  10. Simplicity; maximizing the amount of work done
  11. The best architecture, requirements and designs come from self-organizing teams
  12. The team should regularly reflect on how to become more effective and then act on this

Scrum

Values:

Team:

Initial startup:

Backlog:

Process

Before SP:

SP1:

SP2:

Metrics must be understood and have value to the team:

Refactor: increases code quality; changes function without its behavior. Should be low risk and done incrementally.

Re-engineering: fixing behavioral issues

Tracking:

Misc:

Review:

Retro. Discuss:

Smells:

User Stories

A promise of a conversation to be had with a hypothetical user. Should discussed between PO and dev team.

As a role, I action so that value

Users:

Use Case Scenarios

User stories are partial scenarios that focus on result; use cases scenarios are more detailed and focus on specifics.

Use case scenarios can be modelled as robustness diagrams:

  웃 ------ |-o  -----> ⥀ ----- o̲ ◆-----> o
actor    boundary   control   entity  property

OR:

INVEST

Kanban

More task-oriented than Scrum; less time required for initial startup.

Kanban has continuous flow and delivery and no notion of sprints; just tasks.

Principles:

Practices:

Metrics:

Lean

  1. Eliminate waste; no value to customer = waste e.g. partially done work, unnecessary code, bureaucracy, bad communication
  2. Amplify learning: short feedback loops And create knowledge: document decisions and reasoning
  3. Defer decisions; you will have more information in the future
  4. Quality first, not as an afterthought
  5. Respect people; value their opinions, communicate proactively and have some amount of conflict
  6. Deliver fast; identify bottlenecks, create a MVP so the customer can give feedback
  7. Optimize the whole; global, not local maxima

Testing

Validation vs verification:

Fit for purpose:

Agile Testing: automatic unit/acceptance tests, plus manual testing on RCs

Traditional Testing

Unit testing:

Component Testing:

System testing:

Load testing: test behavior/performance in normal/extreme loads to find bottlenecks

Stress testing: under unfavorable conditions

Capacity testing: if it can handle the expected amount of traffic

Reliability and Resilience

Faults, errors, failures:

Availability vs reliability:

Working as designed: specifications may be wrong (not what the user wants) or erroneous (typo)

Improving reliability:

4 Rs for Resilience Engineering:

Security:

CI/CD

Make your builds disposable - updating to a new build should be so easy that it doesn’t matter if you need to do it once or 20 times. Requires deployment and rollback scripts to be written

Deployment:

Paperwork:

Reducing downtime:

Blue-Green:

Canary:

UX

Honeycomb:

Misc:

Stages of Design

Wireframes: low-fidelity sketch; flow between screens. Be extra careful who you engage with; if you show the wireframes, everyone will have different nitpicks and complaints.

Mock-ups: high-fidelity representations based off of the wireframes; no interactivity. Focus on the visual identify.

Prototype: limited functional implementation (e.g. mocking) but with a functioning workflow. Concentrate on visual behavior, risky features.

Weekly Readings

  1. Basket of Options
    • Reduce dependencies: if the first fails, you want to be able to still take advantage of the second
    • Key Results: reduce sandbagging by expecting people to achieve only some of their goals, not all or none
  2. Stand-ups
    • Goals
      • Shared understanding of goals
      • Coordination
      • Share problems/improvements
      • Team bonding
    • Who
      • Anyone involved in day-to-day operations, ensure they don’t disrupt stand-up, may be more helpful for some to view burn-down charts etc.
      • Work items - story-focused stand-up. People speak for the work items. Not everyone needs to speak, hiding problems or shy people
    • What
      • 3 Q: accomplished yesterday, do today, obstacles. Order varies, may have additional questions e.g. code smells spotted
      • Improvement board: public chart identifying obstacles and their progress. Avoid putting down problems the team has no control over
    • Order
      • Last first: encourages punctuality but likely to be unprepared
      • Round robin: enforces notion of self-organizing team with no leader
      • Pass the token: randomness encourages people to focus as when their turn is unknown. Difficult with larger teams
      • Card: pass the token, but nothing to catch and no coffee to spill
      • Walk the board: work items, not people attend. Move through work items ordered by stage reversed (e.g. review, then in progress) and priority (highest first). Blockers, emergency items and stuck items should go first. Danger of reporting to leader
    • When/where
      • Where the work happens, or in front of the story wall
      • Same place/time; don’t wait for stragglers
      • Start the day: difficult with flexible work hours. If not at the start of the day, trap of no work getting done until the stand-up
      • < 15 minutes
      • Signal ending
      • Move discussions outside of stand-up (procedures such as consistent ‘take it offline’ phrase, raising hand)
    • Autonomy; avoid having a leader: rotate facilitator, facilitator should avoid eye-contact to encourage speaker to talk to entire team
    • Focus on tasks, not people: trap of focusing on what they are doing and not if the work they do has value
    • Obstacles should be raised (forgetting, high ‘pain’ threshold, trust) and not just be raised in the stand-up, and take actions to remove them
  3. Domain-Driven Design
    • Draw the business problem: pseudo-UML diagram, boxes and lines etc.
    • Code: model… models, go back and forth between the diagram and code. Notice but don’t try too hard to avoid framework/plumbing stuff polluting the model
    • Co-design with domain experts. Note down the verbs and nouns they use; use it in your model
      • The expert is right, the model is wrong
        • Or the model is trying to solve multiple problems; split the model into two (there will be duplication), go through the process again
  4. Test Principles
    • Fast: sub-second; long enough to lose focus, not long enough to start something
    • Deterministic: policy of deleting non-deterministic tests?
    • Sensitive to behavioral changes, insensitive to structural changes
    • Cheap to write, read, change
  5. Code Reviews
    • Waiting for feedback is a pain
    • No one is a full-time reviewer
    • Not counted as ‘actual’ work
    • Not valuing good reviews
    • Reviewer new to codebase, not known if someone else is reviewing
    • Too big
    • Not understanding motivation for change
    • Bikeshedding - focusing on minor issues e.g. style and overlooking large ones
    • Face-to-face meetings to reach consensus
  6. Communicating Architecture
    • Architects spend time on:
      • Internal work: deep work
      • Inwards communication: listening, reading, asking questions
      • Outwards communication: presenting, documenting, outputting information
    • 50:25:25 is good balance
    • Too much internal thinking - impractical even if structure is good
    • Too much communication - consultant, no solid thinking behind architecture
    • Async communication:
      • Writing scales well (video etc. not often used professionally)
      • Record - records decisions that were made
      • Avoid focusing too heavily on diagrams - requires textual explanation
    • Messaging: engineers need to understand architecture at a concrete level; failure of architect if this is not the case - i.e. burden of communication is on the sender
  7. (Not) Self-Documenting Code
    • Self-explanatory names should tell you what is does or what it is
    • Comments should focus on why and how (implementation details) it does it
    • Comments are part of the code and should be updated in lock-step
  8. YAGNI
    • When building something unnecessary for now, consider:
      • Cost of building: how much time will it take to add the extra extensibility
      • Cost of delay: how adding the feature will delay other features that would otherwise be ready and generating revenue
      • Cost of carry: the extension points will make the system harder to work with
      • Cost of repair: if the extension point was written wrong
      • Cost of refactoring: will it really be that much work to add it in the future?
  9. TDD
    • TDD is the fastest, best way to build software i.e. cheaper
    • Rely on individual judgement
    • Internal quality and productivity directly correlated
    • Test a chain by its links: if each link works, then the whole chain must too
    • Testing should steer design; consider testability as a factor when designing systems
  10. Code Coverage
    • Acts as a reasonable, objective and well-supported metric of test quality
    • Increased code coverage correlates with reduced defects - encouraging testability leads to better modularity etc.
    • High code coverage alone does not mean quality tests
    • … but low code coverage does mean code is untested
    • Pick code coverage based on criticality of code, how often the code will be updated, how long the code is expected to be used for
      • Frequently changing code should be covered; per-commit coverage should be high to ensure project coverage increases over time
    • Aggregate full code coverage (unit, integration, system tests) to avoid thinking total coverage is higher than it actually is
    • Diminishing returns as code coverage increases
    • Legacy code base? Leave it cleaner than you found it
    • Code coverage too low? Don’t deploy it. Ensure it can actually be met so that it doesn’t become a rubber stamp
  11. ACM Code of Ethics
    1. Act in the public interest
    2. Act in the best interests of the client/employer
    3. Product should meet highest professional standards
    4. Maintain integrity an independence in judgement
    5. Managers/leaders should promote ethical approaches to software development and management
    6. Act in the best interests (integrity/reputation) of the profession
    7. Be fair and supportive to colleagues
    8. Self lifelong learning, promote ethical approach
  12. Gebru Google Departure
    • Wrote paper on unintended consequences of some NLP systems (including ones used in Google search) and environmental impacts
    • Rejected by internal review for ignoring relevant research
    • Gebru’s concerns not addressed, threatened to resign
    • Sent internal memo criticizing, fired by Google
  13. Gender Differences and Bias in Accepted Open Source Pull Requests
    • Women’s pull requests accepted more often then men when not identified as women
      • Theories: survivorship bias, self-selection bias, women being held to higher standards
  14. Git Flow Branching
    • Master
      • Always production-ready
    • Dev
    • Feature
      • Pull and push from/to dev
      • When merging use --no-ff (no fast-forward): makes it easier to revert features
    • Release
      • Branched off dev
      • Can get bug fixes
        • Pushed back to dev
      • Merge into master
    • Hotfix
      • Branched off master
      • Changes pushed to master and dev
  15. Git Rebase
    • Reapplies all commits to the tip of another branch
    • Previous commits exist but aren’t accessible
    • If remote branch exists, force push required
    • Never rebase a shared branch - requires a lot of merges and duplicated commits
  16. Chaos Engineering
    • Partition the system into a control and experimental group
      • Yes, in production
      • Ensure the blast radius is minimized
    • In the experimental group, add variables simulating crashes, network disconnects, large traffic spikes etc.
      • Prioritize by impact and frequency
    • Look for a difference between the two groups (and hope the control group hasn’t crashed)
  17. Be Kind in Code Review
    • Assume good faith
      • Comment on code, not developer
      • Don’t use ‘obviously’/‘clearly’
      • Be clear - assume low-context culture
    • If code needs to be explained by author, it probably needs to be rewritten to be more clear
    • Code reviewer has power. If abused, can lead to current contributors to become de-motivated and scares away new contributors. Leads to fewer, less diverse set of contributors and slower progress on the code front
  18. Writing Pull Requests
    • Plan the change
      • Talk to others - gives them context and allows solutions to be brainstormed
    • Pick relevant reviewers. They should have:
      • Worked on it
      • Worked on something related to it
      • Understand what’s being changed
    • Explain - summary and description of change
      • Give context (e.g. issue tracker link)
      • Long != good
      • Guide readers; where is the most important change? What is just method renaming?
    • Small:
      • Don’t mix in unrelated changes
      • Isolate related into multiple merge requests if possible
    • Ready:
      • Ensure it meets DoD
      • Once feedback received, make a new merge request
  19. Rubber Duck Debugging
    • Explain the code to the duck line by line
    • Realize the code wasn’t actually doing what you thought it was doing
    • Thank the duck
  20. Questions to Ask Bugs
    • What is the pattern?
      • Where else does it exist? Where are its siblings? Are there parallel paths that have the same pattern? Commit genocide
    • What is its impact?
      • Fallout to users
      • Cost in productivity
      • Follow-up with users, team, stakeholders
    • Preventing more bugs:
      • Why did it get through your existing process? What can be changed?
      • Can that class of bug be removed?

Design Principles

Cohesion: data + behavior together.

Coupling: information hiding, separation of concerns, independence between modules

Push and pull between keeping related data together and separation of concerns.

Biggest issue in software design: complexity. Solution: decomposition

Nucleus-centred (OO) design: decide what the critical core of the program is and build interfaces around it. The core should be constant while details that have competing solutions are behind interfaces.

Information Hiding:

Tell, Don’t Ask:

Composition over Inheritance:

Over-specialization: use the most general interface you can.

SOLID:

Design Patterns

A solution to a problem in a context. They are:

Defining a pattern:

Documenting:

Documenting instances: