Principles of Software Construction: Objects, Design, and Concurrency��The Last One:
Looking Back & Looking Forward��Claire Le Goues Jeremy Lacomis
1
17-214/514
Administrivia?
2
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Introduction, Overview, and Syllabus��Jeremy Lacomis and Claire Le Goues
3
17-214/514
Modern Software Engineering
Nobody wants to write a million lines of code.
Instead, you use libraries
And your libraries use libraries
4
17-214/514
17-214/514: From Programs to Applications & Systems
Writing algorithms, data structures from scratch
Functions with inputs �and outputs
Sequential and local computation�
Full functional specifications
Reuse of libraries, �frameworks�
Asynchronous and �reactive designs
Parallel and distributed computation
Partial, composable, �targeted models
Our goal: understanding both the building blocks and also the �design principles for construction of software systems at scale
5
17-214/514
https://softwareengineering.stackexchange.com/questions/370135
6
17-214/514
Which version is better?
static void sort(int[] list, boolean ascending) {
… � boolean mustSwap;� if (ascending) {
mustSwap = list[i] > list[j];
} else {
mustSwap = list[i] < list[j];
}
…
}
interface Order {
boolean lessThan(int i, int j);
}
class AscendingOrder implements Order {
public boolean lessThan(int i, int j) { return i < j; }
}
class DescendingOrder implements Order {
public boolean lessThan(int i, int j) { return i > j; }
}
static void sort(int[] list, Order order) {
… � boolean mustSwap =
order.lessThan(list[j], list[i]);
…
}
Version A:
Version B:
7
17-214/514
Some qualities of interest, i.e., design goals
Functional correctness | Adherence of implementation to the specifications |
Robustness | Ability to handle anomalous events |
Flexibility | Ability to accommodate changes in specifications |
Reusability | Ability to be reused in another application |
Efficiency | Satisfaction of speed and storage requirements |
Scalability | Ability to serve as the basis of a larger version of the application |
Security | Level of consideration of application security |
Source: Braude, Bernstein,
Software Engineering. Wiley 2011
8
17-214/514
it depends
Depends on what?
What are scenarios?
What are tradeoffs?
In this specific case, what would you recommend?
(Engineering judgement)
9
17-214/514
"Software engineering is the branch of computer science that creates practical, cost-effective solutions to computing and information processing problems, preferentially by applying scientific knowledge, developing software systems in the service of mankind.
Software engineering entails making decisions under constraints of limited time, knowledge, and resources. […]
Engineering quality resides in engineering judgment. […]
Quality of the software product depends on the engineer's faithfulness to the engineered artifact. […]
Engineering requires reconciling conflicting constraints. […]
Engineering skills improve as a result of careful systematic reflection on experience. […]
Costs and time constraints matter, not just capability. […]
Software Engineering for the 21st Century: A basis for rethinking the curriculum
Manifesto, CMU-ISRI-05-108
10
17-214/514
Semester overview
Crosscutting topics:
11
17-214/514
int a = 010 + 3;
System.out.println("A" + a);
const a = 010 + 3;
console.log("A" + a);
12
17-214/514
Homework & Exams
6 homeworks, 4 smaller + 2 large (with milestones), 1000 points total
(1) intro, (2a) first design (2b) peer review, (3) testing, (4) refactoring, �(2c) final design, (5) concurrency + GUI, (6) extended design + GUI
Homework milestones usually due Mondays, see course calendar
Homework 1 due September 9 at 11:59pm
Two midterms + final (Do not make plans before you know the finals schedule)
M2
1
3
2a
M1
4
5
6a
6b
F
2b
2c
13
17-214/514
Principles of Software Construction �(Design for change, class level)��Starting with Objects
(dynamic dispatch, encapsulation, entry points)�
Jeremy Lacomis, Claire Le Goues
14
17-214/514
TypeScript
Java
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
let message: string = "Hello, World!"; console.log(message);
15
17-214/514
Programming with primitives in Java looks a lot like any other imperative programming.
1 public class TrailingZeros {
2 public static void main(String[] args) {
3 int i = Integer.parseInt(args[0]);
4 System.out.println(trailingZerosInFactorial(i));
5 }
6 static int trailingZerosInFactorial(int i) {
7 int result = 0; // Conventional name for return value
8 while (i >= 5) {
9 i /= 5; // Same as i = i / 5; Remainder discarded
10 result += i;
11 }
12 return result;
13 }
14 }
16
17-214/514
Objects respond to messages, methods define interface
const obj = {
v: 1,
inc: function() { this.v++; },
get: function() { return this.v; },
add: function(y) { return this.v + y; }
}
obj.get() + 2
// 3
obj.add(obj.get()+2)
// 4
obj.send()
// Uncaught TypeError: obj.send is not a function
Calling a method that does not exist results in an error
This is JavaScript code!
17
17-214/514
Multiple Implementations of Interface
interface Point {
int getX();
int getY();
}
class PolarPoint implements Point {
double len, angle;
PolarPoint(double len, double angle)
{ this.len=len; this.angle=angle; }
int getX() { return this.len * cos(this.angle); }
int getY() { return this.len * sin(this.angle); }
double getAngle() {...}
}
Point p = new PolarPoint(5, .245);
This is Java code!
18
17-214/514
Method Call Internals
l: Line
�points: Point[]
draw(Canvas)
s0: PolarPoint
�angle
len
getX()
getY()
s1: CartesianPoint
�x
y
getX()
getY()
Using the object’s own method implementation,
not a fixed jump to an address
19
17-214/514
Flexibility of dynamic dispatch (Java)
Each class decides implementation, �client does not care
Static methods are global functions, only single copy exists; class provides only namespace
Java does not allow global functions outside of classes
interface Point {
void move(int x, int y) { ... }
}
class Helper {
static void movePoint(Point p,
int x, int y) {...}
}
Point p = createPoint(...);
// dynamic dispatch, object’s method
p.move(4, 5);
// single global method, less flexible
Helper.movePoint(p, 4, 5);
The “main” function is defined this way!
This is Java code!
Design for Change!
20
17-214/514
JavaScript: �Closures for Hiding
All methods and fields are public, no language constructs for access control
TypeScript added them, so it’s quite similar to Java!
In JS: Encoding hiding with closures
function createPolarPoint(len, angle) {
let xcache = -1;
let internalLen = len;
function computeX() {...}
return {
getX: function() {
computeX(); return xcache; },
getY: function() {
return len * sin(angle); }
};
}
const pp = createPolarPoint(1, 0);
pp.getX(); // works
pp.computeX(); // runtime error
pp.xcache // undefined
pp.len // undefined
21
17-214/514
Starting a Program: Javascript
Objects do not do anything on their own, they wait for method calls
Every program needs a starting point, or waits for events
// start with: node file.js
function createPrinter() {
return {
print: function() { console.log("hi"); }
}
}
const printer = createPrinter();
printer.print();
// hi
Defining interfaces,
functions, classes
Starting:
Creating objects and
calling methods
This is Typescript code!
22
17-214/514
Starting a program: Java
All Java code is in classes, so how to create an object and call a method?
Special syntax for main method in class (java X calls main in X)
// start with: java Printer
class Printer {
void print() {
System.out.println("hi");
}
public static void main(String[] args) {
Printer obj = new Printer();
obj.print();
}
}
Main method to be
executed, here used to
create object and invoke
method
Static methods belong to
class not the object,
generally avoid them
This is Java code!
in Java, everything is a class
main must be public and static
23
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Object-oriented Analysis
�Jeremy Lacomis and Claire Le Goues
24
17-214/514
25
17-214/514
Input to the analysis process: Requirements and use cases
Time to start�coding?
26
17-214/514
Problem
Space
(Domain Model)
Solution
Space
(Object Model)
27
17-214/514
Visual notation: UML
Library Account
accountID
lateFees
Name of real-world concept
(not software class)
Properties of concept
Book
title
author
borrow
1
*
Associations between concepts
Multiplicities/cardinalities
indicate “how many”
28
17-214/514
UML Sequence Diagram Notation
User
System
Actors in this �use case (systems and real-world objects/people)
Messages and responses for interactions,
text describes what happens conceptually
Time proceeds from top to bottom
login(card)
borrow(book)
success?, due date
29
17-214/514
System behavioral contract example
Operation: borrow(item)
Pre-conditions: Library member has already logged in to the system.
Item is not currently borrowed by another member.
Post-conditions: Logged-in member's account records the� newly-borrowed item, or the member is warned she has an� outstanding late fee.
The newly-borrowed item contains a future due date,� computed as the item's rental period plus the current date.
30
17-214/514
Low Representational Gap
Identified concepts provide inspiration for classes in the implementation
Classes mirroring domain concepts often �intuitive to understand:�Low representational gap principle
class Account {
id: int;
lateFees: int;
borrowed: List<Book>;
boolean borrow(Book) {...}
void save();
}
class Book {...}
Library Account
accountID
lateFees
Book
title
author
borrow
1
*
31
17-214/514
Design Goals, Principles, and Patterns
Get familiar with design terminology – we’ll see a lot of these
32
Goals
Heuristics
Patterns
Principles
32
17-214/514
Principles of Software Construction��Responsibility assignment��Jeremy Lacomis, Claire Le Goues
33
17-214/514
Domain model (left) vs. object model (right)
34
17-214/514
Object-level interaction diagrams
35
17-214/514
Representational gap
36
17-214/514
Visualizing coupling
37
17-214/514
Visualizing cohesion
38
17-214/514
Anti-pattern: God object
class Chat {
List<String> channels;
Map<String, List<Msg>> messages;
Map<String, String> accounts;
Set<String> bannedUsers;
File logFile;
File bannedWords;
URL serverAddress;
Map<String, Int> globalSettings;
Map<String, Int> userSettings;
Map<String, Graphic> smileys;
CryptStrategy encryption;
Widget sendButton, messageList;
}
39
17-214/514
DESIGN HEURISTIC: controller
40
17-214/514
DESIGN HEURISTIC: Information expert
41
17-214/514
class Shipment {
private List<Box> boxes;
int getWeight() {
int w=0;
for (Box box: boxes)
for (Item item: box.getItems())
w += item.weight;
return w;
}
class Box {
private List<Item> items;
Iterable<Item> getItems() { return items;}
}
class Item {
String description;
int weight;
}
“Quiz”: Critique this design: https://tinyurl.com/214-quiz4
Access code: shipment
42
17-214/514
A creator example
43
17-214/514
Which design is better? Argue with design goals, principles, heuristics, and patterns that you know
* old midterm question
44
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Inheritance and delegation��Claire Le Goues and Jeremy Lacomis
45
17-214/514
Quick Santorini demonstration
46
17-214/514
All object types exist in a class hierarchy
In Java:
Object
Collection
Error
List
RuntimeError
Exception
47
17-214/514
Using Inheritance: LoggingList
public class LoggingList extends IntArrayList {
@Override
public void add(int i) {
System.out.println("adding " + i);
super.add(i);
}
}
LoggingList is a special kind of IntArrayList. It is a subclass of IntArrayList and inherits all of the fields and methods of IntArrayList.
We override the implementation of add to incorporate logging functionality.
super means that method calls made on it dispatch to the superclass implementation from IntArrayList (contrast with this) .
48
17-214/514
“Can I inherit from this type?”
Yes.
49
17-214/514
Behavioral Subtyping gives a more formal principle behind when extension should be considered.
Animal dog = new Dog();
The Liskov substitution principle:
“Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.”
Barbara Liskov
Roughly:
50
17-214/514
Design option 2
abstract class GenericCard
implements PaymentCard {
…
public String getCardHolderName() {
return this.cardHolderName;
}
public BigInteger getDigits() {
return this.digits;
}
public Date getExpiration() {
return this.expirationDate;
}
abstract boolean pay(int amount);
}
PaymentCard
GenericCard
CreditCard
DeditCard
class CreditCard extends GenericCard {
@Override
public boolean pay(int amount) {
…
}
}
class DebitCard extends AbstractGenericCard {
@Override
public boolean pay(int amount) {
…
}
}
Much more reuse; inheritance is probably a good choice here.
But not always!
51
17-214/514
Design option 3
class CreditCard implements PaymentCard {
private CardData cardData = new(…);
public BigInteger getDigits() {
return cardData.getDigits();
}
…
}
class DebitCard implements PaymentCard {
…
}
PaymentCard
CardData
CreditCard
DeditCard
You can still achieve good reuse with composition+delegation!
class CardData {
private final String cardHolderName;
private final BigInteger digits;
private final Date expirationDate;
public CardData(…) {…}
public String getCardHolderName() {…}
public BigInteger getDigits() {…}
public Date getExpiration() {…}
}
Is this better?
52
17-214/514
Template Method vs. Strategy Pattern
53
17-214/514
Strategy Pattern in UML.
Context
Strategy
execute()
ConcreteStrA
ConcreteStrB
algorithm()
execute()
execute()
54
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Design Patterns��Jeremy Lacomis and Claire Le Goues
55
17-214/514
Discussion with design patterns
56
17-214/514
History: �Design Patterns �(1994)
57
17-214/514
58
17-214/514
Strategy Pattern vs Higher-Order Function
const ASC =
function(i: number, j: number): boolean {
return i < j;
}
const DESC = � function(i: number, j: number): boolean {
return i > j;
}
function sort(
list: number[],
order: (number, number) => boolean) ...;
interface Order {� boolean lessThan(int i, int j);
}
class AscendingOrder implements Order {
public boolean lessThan(int i, int j) {
return i < j; }
}
class DescendingOrder implements Order {
public boolean lessThan(int i, int j) {
return i > j; }
}
void sort(int[] list, Order order) ;
59
17-214/514
The Template Method design pattern
60
17-214/514
Inheritance vs. Composition + Delegation
61
17-214/514
The Composite Design Pattern
62
17-214/514
We have seen this before
function newCombinedCardOrganizer (cardOrganizers: CardOrganizer[]): CardOrganizer {
return {
reorganize: function (cards: CardStatus[]): CardStatus[] {
let status = cards.slice()
for (const cardOrganizer of cardOrganizers) {
status = cardOrganizer.reorganize(status)
}
return status
}
}
}
63
17-214/514
Module pattern: Hide internals in closure
Function provides local scope, internals not accessible
Function directly invoked to execute it once
Wrapped in parentheses to make it expression
Discovered around 2007, became very popular, part of Node
(function () {
// ... all vars and functions are in this scope only
// still maintains access to all globals
}());
64
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Design Practice��Jeremy Lacomis and Claire Le Goues
65
17-214/514
Limitations of inheritance
Cannot combine features
Intermediate functionality required
66
17-214/514
The Decorator design pattern
Common interface for both wrappers and wrapped objects
Class of objects being wrapped. Defines basic behavior that can be altered by decorators
Define extra behaviors that can be added to components dynamically. Override methods of the base decorator and execute their behavior either before or after calling the parent method.
Has a wrappee field for referencing wrapped objects. The type is the interface so it can contain both concrete components and decorators. The base decorator delegates all operations to the wrapped object.
The client can wrap components in multiple layers of decorators, as long as they work with all objects via the component interface.
67
17-214/514
Trouble Rules
Home
Finish
68
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Refactoring & Anti-patterns��Jeremy Lacomis and Claire Le Goues
69
17-214/514
The Java class hierarchy
�
Object
Game
Instrument
Santorini
Guitar
70
17-214/514
How could this code be improved?
class Player {
Board board;
/* in code somewhere… */
this.getSquare(n);
Square getSquare(String name) {
// named monopoly squares
for (Square s: board.getSquares())
if (s.getName().equals(name))
return s;
return null;
}
}
71
17-214/514
Refactoring: IDE Support
72
17-214/514
Example: instanceof
class Animal { }
class Dog extends Animal {
public Dog() { super("dog"); }
public String bark() {
return "Woof!";
}
}
// Example 3:
if (animal instanceof Dog)
((Dog) animal).bark();
// Example 2:
((Dog) animal).bark();
// Example 1:
Animal animal = new Dog();
animal.bark();
73
17-214/514
Liquid APIs
Each method changes �state, �then returns this
(Immutable version: �Return modified copy)
class OptBuilder {
private String argName = "";
private boolean hasArg = false;
...
OptBuilder withArgName(String n) {
this.argName = n;
return this;
}
OptBuilder hasArg() {
this.hasArg = true;
return this;
}
...
Option create() {
return new Option(argName,� hasArgs, ...)
}
}
74
17-214/514
Iterator Pattern & Streams
(what’s up with for(Person p : this.records)?)
75
17-214/514
Principles of Software Construction��Specifications, unit testing, exceptions��Claire Le Goues, Jeremy Lacomis
76
17-214/514
Who’s to blame?
/**
* This method finds the shortest
* distance between two vertices.
* It returns -1 if the two nodes
* are not connected.
*/
function shortestDistance(…): number {…}
Think of this (textual) specification as a “contract”
77
17-214/514
Most real-world code has a contract
Service* �implementation
Service interface
Client�environment
Hidden from �service provider
Hidden from �service client
* service = object, �subsystem, …
78
17-214/514
Control-flow of exceptions
Handle errors at a level you choose, not necessarily in the low-level methods where they originally occur.
public static void test() {
try {
System.out.println("Top");
int[] a = new int[10];
a[42] = 42;
System.out.println("Bottom");
} catch (NegativeArraySizeException e) {
System.out.println("Caught negative array size");
}
}
public static void main(String[] args) {
try {
test();
} catch (IndexOutOfBoundsException e) {
System.out.println"("Caught index out of bounds");
}
}
This is Java code
79
17-214/514
Java’s exception hierarchy
Throwable
Exception
RuntimeException
IOException
EOFException
FileNotFoundException
NullPointerException
IndexOutOfBoundsException
ClassNotFoundException
Object
Error
StackOverflowError
…
…
…
…
Checked Exceptions
80
17-214/514
Quiz: Check your understanding
public class Main {
private String str = "";
public void A() {
str += "a";
try {
str += "b";
B();
} catch (Exception e) {
str += "c";
}
str += "d";
}
public void B() throws Exception {
try {
str += "d";
C();
} catch (IndexOutOfBoundsException e) {
str += "e";
} finally {
str += "f";
}
str += "g";
}
public void C() throws Exception {
int x = 1 / 0;
}
public void display() {
System.out.println(str);
}
public static void main(String[] args) {
try {
Main test = new Main();
test.A();
test.display();
} catch (Exception e) {
System.out.println("nothing");
}
}
}
81
17-214/514
Re: Formal verification, Testing
“Testing shows the presence, not the absence of bugs.”
Edsger W. Dijkstra, 1969
“Beware of bugs in the above code; I� have only proved it correct, not tried it.”
Donald Knuth, 1977
82
17-214/514
Test boundary values
If you cannot test every input:
Choose representative values:�1 for positives, -1 for negatives
And boundary cases: 0 is a likely�candidate for mistakes
boolean isPositive(int x) {
return x >= 0; // What if?
}
@Test
public void test1IsPos() {
assertTrue(isPositive(1));
}
@Test
public void test0IsNotPos() {
assertFalse(isPositive(0)); // Fails
}
83
17-214/514
For Java: JUnit
Syntax:
import static org.junit.Assert.*;
class PosTests {
@Before
void setUp() {
// Anything you want to run
before each test
}
@Test
void test1IsPos() {
assertTrue(isPos(1));
}
}
This is Java code
84
17-214/514
Specification vs. Structural Testing
/**
* Checks if the provided card has been answered correctly the required number of times.
* @param card The {@link CardStatus} object to check.
* @return {@code true} if this card has been answered correctly at least {@code this.repetitions} times.
*/
public boolean isComplete(CardStatus card) {
return card.getSuccesses.get(0); // <-- Bad, but passes both tests
}
This is Java code
85
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Test case design��Claire Le Goues, Jeremy Lacomis
86
17-214/514
Use the specification for testing
/**
* Checks if the provided card has been answered correctly the required number of times.
* @param card The {@link CardStatus} object to check.
* @return {@code true} if this card has been answered correctly at least {@code this.repetitions} times.
*/
public boolean isComplete(CardStatus card);
// What is specified?
// - Parameter type (no constraints)
// - Return constraints: “at least” this.repetitions correct answers
// So what do we test?
87
17-214/514
There also exist strategies to identify plausible mistakes
/** Returns true and subtracts cost if enough � * money is available, false otherwise.
*/
public boolean pay(int cost) {
if (cost < this.money) {
this.money -= cost;
return true;
}
return false;
}
88
17-214/514
Decision Tables
We need a strategy to identify plausible mistakes
Test case | useCredit | enough Credit | enough Cash | Result |
1 | T | T | - | Pass |
2 | F | - | T | Pass |
3 | F | - | F | Fails |
4 | T | F | T | Pass |
5 | T | F | F | Fails |
89
17-214/514
CreditWallet.pay()
public boolean pay(int cost, boolean useCredit) {
if (useCredit) {
if (enoughCredit) {
return true;
}
}
if (enoughCash) {
return true;
}
return false;
}
Test case | useCredit | enough Credit | enough Cash | Result | Coverage |
1 | T | T | - | Pass | -- |
2 | F | - | T | Pass | -- |
3 | F | - | F | Fails | Statement |
90
17-214/514
Specification vs. Structural testing
Structural: five paths through the code.
Specification: possibly only 3.
What do you think?
/** Pays with credit if useCredit is set and enough
* credit is available; otherwise, pays with cash if
* enough cash is available; otherwise, returns false.
*/
public boolean pay(int cost, boolean useCredit);
91
17-214/514
Real-world systems are complex.
92
17-214/514
Eliminating the Android Dependency
Code
Test Driver
@Test void testGetFriends() {
assert getFriends() == ...;
}
List<Friend> getFriends() {
Connection c = http.getConnection();
FacebookAPI api = new FacebookAPI(c);
return api.getFriends("john");
}
93
17-214/514
There are Several Kinds of Test Double
94
https://martinfowler.com/articles/mocksArentStubs.html
http://xunitpatterns.com/Test%20Double%20Patterns.html
https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da?gi=b7a3c3a0c968
94
17-214/514
Test Double: Mock
Used to test for expected interactions with a collaborator (i.e., method calls). Can behave like a spy, a stub, or both.
95
// Pass in a mock that was created by a mocking framework.�AccessManager accessManager = new AccessManager(mockAuthenticationService);
accessManager.userHasAccess(USER_ID);�
// The test should fail if accessManager.userHasAccess(USER_ID) didn't call
// authenticationService.isAuthenticated(USER_ID) or if it called it more than once.
verify(mockAuthenticationService).isAuthenticated(USER_ID);
https://testing.googleblog.com/2013/07/testing-on-toilet-know-your-test-doubles.html
95
17-214/514
Which Type Was This?
Code
FacebookInterface
Test Driver
@Test void testGetFriends() {
assert getFriends() == ...;
}
List<Friend> getFriends() {
Connection c = http.getConnection();
FacebookAPI api = new Facebook???(c);
return api.getFriends("john");
}
Facebook???
class Facebook???� implements FacebookAPI {
void connect() {}
List<Node> getFriends(String name) {
if (name.equals("john")) {
return List.of(...);
} // ...
}
}
96
17-214/514
Fault injection
Code
Mock Facebook
Test driver
class FacebookErrorStub implements FacebookAPI {
void connect() {}
int counter = 0;
List<Node> getFriends(String name) {
counter++;
if (counter % 3 == 0)
throw new SocketException("Network is unreachable");
else if (name.equals("john")) {
return List.of(...);
} // ...
}
}
97
17-214/514
Use A Mocking Framework For Your Test Doubles
98
98
17-214/514
The test Double Cheatsheet
99
99
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Testability��Claire Le Goues, Jeremy Lacomis
100
17-214/514
Testing Queries and Commands
Testing queries is easy because we only care about responses
Replace expensive dependencies and to define their behaviors for the different scenarios
Testing commands is harder
Need to verify that expected outcome by looking for effect on the system
101
17-214/514
Design principle for testable code: SOLID
102
17-214/514
Classic OCP example.
Bad Example
Good Example
class GraphicEditor {� public void drawShape(Shape s)
{ s.draw(); }�}��class Shape {� abstract void draw();�}��class Rectangle extends Shape {� public void draw() {...}�}
class GraphicEditor {� public void drawShape(Shape s) {� if (s.m_type==1) drawRectangle(s);� else if (s.m_type==2) drawCircle(s);� }� public void drawCircle(Circle r) {....}� public void drawRectangle(Rectangle r)
{....}�}��class Shape {� int m_type;�}�class Rectangle extends Shape {� Rectangle() { super.m_type=1; }�}��class Circle extends Shape {� Circle() { super.m_type=2; }�}
103
17-214/514
Dependency Injection
104
..is giving an object its instance variables (James Shore)
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
} defined
public void doStuff() {
...
myDatabase.getData();
... used
}
}
Dependency: class Example depends on an instance of DatabaseThingy
http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
Unfortunately, it is defined (instantiated) in the same class it’s used
Can’t double myDatabase when testing Example class!
How can you fix this?
104
17-214/514
Design: System should enforce clear separation of responsibilities
External service
External service
Hexagonal/Ports and Adapters pattern:
105
17-214/514
Good Testing Practices
106
17-214/514
Testing offers clues about the quality of your design.
All unit tests do basically three things. If any of them are difficult, can they be changed?
107
17-214/514
Key functionality that plays into testability/debuggability goes beyond how the code is written
If something goes wrong, do you have data to diagnose it?
Can you use that data to reproduce the problem?
If something goes wrong, can you tell?
108
17-214/514
Principles of Software Construction�
�Intro to Concurrency��Jeremy Lacomis and Claire Le Goues
109
17-214/514
Writing Testable Code
What is the problem with this?
public boolean hasHeader(String path) throws IOException {
List<String> lines = Files.readAllLines(Path.of(path));
return !lines.get(0).isEmpty()
}
// to achieve a ‘false’ output without having a test input file:
try {
Path tempFile = Files.createTempFile(null, null);
Files.write(tempFile,"\n".getBytes(StandardCharsets.UTF_8));
hasHeader(tempFile.toFile().getAbsolutePath()); // false
} catch (IOException e) {
e.printStackTrace();
}
110
17-214/514
Three Concepts of Importance
111
17-214/514
A simple threads example
public static void main(String[] args) {
int n = 5; // number of threads
Runnable greeter = new Runnable() {
public void run() {
System.out.println("Hello world");
}
};
for (int i = 0; i < n; i++) {
new Thread(greeter).start();
}
}
112
17-214/514
e.g., concurrent I/O in a machine learning task
Different devices:
113
17-214/514
Safety failure: Money-grab (1)
public class BankAccount {
private long balance;
public BankAccount(long balance) {
this.balance = balance;
}
static void transferFrom(BankAccount source,
BankAccount dest, long amount) {
source.balance -= amount;
dest.balance += amount;
}
public long balance() {
return balance;
}
}
114
17-214/514
Concurrency control with Java’s intrinsic locks
Thread1�Thread2�Thread3
115
17-214/514
Atomicity
i++;
1. Load data from variable i
2. Increment data by 1
3. Store data to variable i
is actually
116
17-214/514
Making a class immutable
public class Complex {
double re, im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public double getRealPart() { return re; }
public double getImaginaryPart() { return im; }
public double setRealPart(double re) { this.re = re; }
public double setImaginaryPart(double im) { this.im = im; }
public void add(Complex c) { re += c.re; im += c.im; }
…
117
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Concurrency and Hazards��Jeremy Lacomis and Claire Le Goues
118
17-214/514
Liveness Hazard
119
17-214/514
Deadlock example
Two threads:
A does transfer(a, b, 10) B does transfer(b, a, 10)
Execution trace:
A: lock a (✓)
B: lock b (✓)
A: lock b (x)
B: lock a (x)
A: wait
B: wait
Deadlock!
class Account {
double balance;
void withdraw(double amount){ balance -= amount; }
void deposit(double amount){ balance += amount; }
void transfer(Account from, Account to, double amount){
synchronized(from) {
from.withdraw(amount);
synchronized(to) {
to.deposit(amount);
}
}
}
}
120
17-214/514
Circular �references & �Caching
Immutable data structures often from a directed acyclic graph
Cycles challenging
Cycles often useful for performance (caching)
class TreeNode {
readonly #parent: TreeNode
readonly #children: TreeNode[]
constructor(parent: TreeNode,
children: TreeNode[]) {
this.#parent = parent
this.#children = children
}
addChild(child: TreeNode) {
const newChildren = this.#children.slice()
//const newChild = child.setParent(this) ??
newChildren.push(child)
const newNode = new TreeNode(this.#parent,
newChildren)
//child.setParent(newNode) ??
return newNode
}
}
121
17-214/514
Performance Hazard
122
17-214/514
What are some disadvantages of this parallel implementation?
123
17-214/514
Principles of Software Construction�
�Java Parallelism��Jeremy Lacomis and Claire Le Goues
124
17-214/514
Amdahl’s law
125
17-214/514
Upsweep
Downsweep
126
17-214/514
Parallel Prefix Sum
127
17-214/514
class MyHouse {
private boolean pizzaArrived = false;
public void eatPizza(){
synchronized(this){
while(!pizzaArrived){
wait();
}
}
System.out.println("yumyum..");
}
public void pizzaGuy(){
synchronized(this){
this.pizzaArrived = true;
notifyAll();
}
}
}
wait on the lock for this instance of MyHouse to be released until pizzaArrived. Release the lock on MyHouse.
While holding the lock on this instance of MyHouse, notify all threads waiting on this lock that they can wake up.
128
17-214/514
2. Concurrent Collections
Unsynchronized | Concurrent |
HashMap | ConcurrentHashMap |
HashSet | ConcurrentHashSet |
TreeMap | ConcurrentSkipListMap |
TreeSet | ConcurrentSkipListSet |
129
17-214/514
5. Synchronizers
130
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Concurrency and Asynchrony in TypeScript��Jeremy Lacomis and Claire Le Goues
131
17-214/514
The JavaScript Engine (e.g., V8)
Two main components:
132
17-214/514
What happens when things are slow?
JavaScript is single threaded (single Call Stack).
Problem: while the Call Stack has functions to execute, the browser can’t actually do anything else — it’s getting blocked.
function downloadIt(url) {
// download the file
let f = fetchIt('big-file.txt');
console.log('Got it');
}
console.log('Start downloading...');
downloadIt('big-file.txt');
console.log('Done!');
Start downloading...
Got it
Done!
Note: this is an illustration–JavaScript libraries don’t actually work like this!
133
17-214/514
Asynchrony in User Interfaces
Callback functions
document.addEventListener('click', () => console.log('Clicked!'))
134
17-214/514
The Event Loop
The state is clear.
console.log('Hi');
setTimeout(function cb1() {
console.log('cb1');
}, 5000);
console.log('Bye');
135
17-214/514
Design Goals
const makeBurger = nextStep => {
getBeef(function (beef) {
cookBeef(beef, function (patty) {
getBuns(function (buns) {
putBeefBetweenBuns(buns, patty, function(burger) {
nextStep(burger)
});
});
});
});
};
// Make and serve the burger
makeBurger(function (burger) => {
serve(burger);
});
136
17-214/514
Solving “Callback Hell” with Promises
function makeBurger() : Promise<Burger> {
return getBeef()
.then(beef => cookBeef(beef))
.then(cookedBeef => getBuns(beef))
.then(bunsAndBeef => putBeefBetweenBuns(bunsAndBeef));
};
// Make and serve the burger
makeBurger().then(burger => serve(burger));
137
17-214/514
Promise-like syntax is in most languages
public class SquareCalculator {
private ExecutorService executor = Executors.newSingleThreadExecutor();
public Future<Integer> calculate(Integer input) {
return executor.submit(() -> {
Thread.sleep(1000);
return input * input;
});
}
}
138
17-214/514
Solving “Callback Hell” with Async/Await
const makeBurger = async () => {
const beef = await getBeef();
const cookedBeef = await cookBeef(beef);
const buns = await getBuns();
const burger = await putBeefBetweenBuns(cookedBeef, buns);
return burger;
};
// Make and serve the burger
makeBurger().then(serve);
139
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Concurrency and Patterns��Jeremy Lacomis and Claire Le Goues
140
17-214/514
The Event Loop Redux
function a() {
console.log("as");
setTimeout(function acb() {
console.log("acb");
}, 10);
console.log("ae");
}
function b() {
console.log("bs");
setTimeout(function bcb() {
console.log("bcb");
}, 0);
a();
console.log("be");
}
b();
b()
141
17-214/514
Async/Await Redux
142
17-214/514
async function getData() {
console.log("A");
const result = await fetchData();
console.log("B");
return result;
}
async function fetchData() {
console.log("C");
return new Promise((resolve) => {
setTimeout(() => {
console.log("D");
resolve("Data");
}, 1000);
});
}
console.log("E");
getData().then((data) => console.log("F"));
console.log("G");
What does this print?
Console:
E
143
17-214/514
Sample problem:
You have a store with customers. The customers are very interested in a particular product (e.g., a new model of computer) that should be available at your store soon.
Imagine you have two objects: Store and Customer. How could the Customer object find out when the new computer arrives at the store?
144
17-214/514
Observer Pattern
145
17-214/514
Proxy Design Pattern
Declares the interface of the Service. The proxy must follow this interface to disguise itself as a service object
146
17-214/514
The Adapter �Design Pattern
Applicability
Consequences
147
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Introduction to GUIs��Claire Le Goues and Jeremy Lacomis
148
17-214/514
How do you GUI? Multiplayer?
https://www.cloudsavvyit.com/2586/how-to-build-your-multiplayer-games-server-architecture/
while (true) {
if (player === “player1”) {
hasWon = play(“player1”);
if (hasWon) break;
player = “player2”;
} else (player === “player2”) {
hasWon = play(“player2”)
if (hasWon) break;
player = “player1”;
}
}
149
17-214/514
Event-based programming
Style of programming where control-flow is driven by (usually external) events
public void performAction(ActionEvent e) {
List<String> lst = Arrays.asList(bar);
foo.peek(42)
}
public void performAction(ActionEvent e) {
bigBloatedPowerPointFunction(e);
withANameSoLongIMadeItTwoMethods(e);
yesIKnowJavaDoesntWorkLikeThat(e);
}
public void performAction(ActionEvent e) {
List<String> lst = Arrays.asList(bar);
foo.peek(40)
}
150
17-214/514
Anatomy of an HTML Page
Nested elements
151
17-214/514
That’s extremely simple, let’s try something slightly more complicated.
Consider: TicTacToe
152
17-214/514
Decoupling with the Observer pattern
153
17-214/514
Actions: JavaScript
154
17-214/514
Web Servers
Dynamic sites can do more work
https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview#anatomy_of_a_dynamic_request
155
17-214/514
An architectural pattern: Model-View-Controller (MVC)
Manage inputs from user: mouse, keyboard, menu, etc.
Manage display of information on the screen
Manage data related to the application domain
156
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Libraries and Frameworks
(Design for large-scale reuse)
Jeremy Lacomis and Claire Le Goues
157
17-214/514
Earlier in this course: Class-level reuse
Language mechanisms supporting reuse
Design principles supporting reuse
Design patterns supporting reuse
158
17-214/514
Reuse and variation: �Visual Studio Code Extensions
159
17-214/514
Reuse and variation: �Product lines
160
17-214/514
A calculator example (without a framework)
public class Calc extends JFrame {
private JTextField textField;
public Calc() {
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
button.setText("calculate");
contentPane.add(button, BorderLayout.EAST);
textField = new JTextField("");
textField.setText("10 / 2 + 6");
textField.setPreferredSize(new Dimension(200, 20));
contentPane.add(textfield, BorderLayout.WEST);
button.addActionListener(/* calculation code */);
this.setContentPane(contentPane);
this.pack();
this.setLocation(100, 100);
this.setTitle("My Great Calculator");
// ...
}
}
161
17-214/514
Using the example framework again
public abstract class Application extends JFrame {
protected String getApplicationTitle() { return ""; }
protected String getButtonText() { return ""; }
protected String getInitialText() { return ""; }
protected void buttonClicked() { }
private JTextField textField;
public Application() {
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
button.setText(getButtonText());
contentPane.add(button, BorderLayout.EAST);
textField = new JTextField("");
textField.setText(getInitialText());
textField.setPreferredSize(new Dimension(200, 20));
contentPane.add(textField, BorderLayout.WEST);
button.addActionListener((e) -> { buttonClicked(); });
this.setContentPane(contentPane);
this.pack();
this.setLocation(100, 100);
this.setTitle(getApplicationTitle());
// ...
}
}
public class Ping extends Application {
protected String getApplicationTitle() { return "Ping"; }
protected String getButtonText() { return "ping"; }
protected String getInititalText() { return "127.0.0.1"; }
protected void buttonClicked() { /* ... */ }
}
162
17-214/514
General distinction: Library vs. framework
Library
Framework
public MyWidget extends JContainer {
ublic MyWidget(int param) {/ setup internals, without rendering
}
/ render component on first view and resizing
protected void paintComponent(Graphics g) {
// draw a red box on his componentDimension d = getSize();
g.setColor(Color.red);
g.drawRect(0, 0, d.getWidth(), d.getHeight()); }
}
public MyWidget extends JContainer {
ublic MyWidget(int param) {/ setup internals, without rendering
}
/ render component on first view and resizing
protected void paintComponent(Graphics g) {
// draw a red box on his componentDimension d = getSize();
g.setColor(Color.red);
g.drawRect(0, 0, d.getWidth(), d.getHeight()); }
}
your code
user
interacts
your code
user
interacts
163
17-214/514
More terms
164
17-214/514
An example blackbox framework
public class Application extends JFrame implements InputProvider {
private JTextField textField;
private Plugin plugin;
public Application() { }
protected void init(Plugin p) {
p.setApplication(this);
this.plugin = p;
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
button.setText(plugin != null ? plugin.getButtonText() : "ok");
contentPane.add(button, BorderLayout.EAST);
textField = new JTextField("");
if (plugin != null) textField.setText(plugin.getInititalText());
textField.setPreferredSize(new Dimension(200, 20));
contentPane.add(textField, BorderLayout.WEST);
if (plugin != null)
button.addActionListener((e) -> { plugin.buttonClicked(); } );
this.setContentPane(contentPane);
// ...
}
public String getInput() { return textField.getText(); }
}
public interface Plugin {
String getApplicationTitle();
String getButtonText();
String getInititalText();
void buttonClicked() ;
void setApplication(InputProvider app);
}
public class CalcPlugin implements Plugin {
private InputProvider app;
public void setApplication(InputProvider app) { this.app = app; }
public String getButtonText() { return "calculate"; }
public String getInititalText() { return "10 / 2 + 6"; }
public void buttonClicked() {
JOptionPane.showMessageDialog(null, "The result of "
+ app.getInput() + " is "
+ calculate(app.getInput()));
}
public String getApplicationTitle() { return "My Great Calculator"; }
}
public interface InputProvider {
String getInput();
}
165
17-214/514
Tangrams
166
17-214/514
The use vs. reuse dilemma
“maximizing reuse minimizes use”
C. Szyperski
167
17-214/514
Example: A JUnit Plugin
public class SampleTest {
private List<String> emptyList;
@Before
public void setUp() {
emptyList = new ArrayList<String>();
}
@After
public void tearDown() {
emptyList = null;
}
@Test
public void testEmptyList() {
assertEquals("Empty list should have 0 elements",
0, emptyList.size());
}
}
Here the important plugin
mechanism is Java
annotations
168
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��API Design I
Jeremy Lacomis and Claire Le Goues
169
17-214/514
Example: A user says this to you
170
17-214/514
Names can be literal or metaphorical
171
17-214/514
172
17-214/514
173
17-214/514
Precondition
Postcondition
174
17-214/514
key not found
key can be null
Does not throw
How it compares keys
175
17-214/514
When you are done, test your documentation!
176
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��API Design II
Jeremy Lacomis and Claire Le Goues
177
17-214/514
Conceptual weight
178
17-214/514
Generalizing an API can make it smaller
public class Vector {
public int indexOf(Object elem, int index);
public int lastIndexOf(Object elem, int index);
}
179
17-214/514
Make it easy to do what’s common, possible to do what’s less common
180
17-214/514
…and impossible to do what is wrong!
inherits public Object put(Object key, Object value); from Hashtable
Also provides a method to save the contents to a file: public void save(OutputStream out, String comments);
181
17-214/514
Examples of “just trust me”
182
17-214/514
Hyrum’s Law
“With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.”
- Hyrum Wright, Google, 2016
183
17-214/514
Even if you never intend to use the API externally, design it as if it were public.
184
17-214/514
What is a Calendar and what does it do?
185
17-214/514
// A collection of elements (root of the collection hierarchy)
public interface Collection<E> {
// Returns true if the collection is empty
boolean isEmpty();
// Ensures that the collection contains e
void add(E o);
// Removes an instance of e from the collection, if present
boolean remove(Object o);
// Returns true iff collection contains e
boolean contains(Object o);
// Returns the number of elements in the collection
int size();
}
186
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Supply Chain Security �and Sustainability��Jeremy Lacomis and Claire Le Goues
187
17-214/514
188
17-214/514
How do you fix it?
public static void main(String [] args) {
BigInteger fiveThousand = new BigInteger("5000");
BigInteger fiftyThousand = new BigInteger("50000");
BigInteger fiveHundredThousand = new BigInteger("500000");
BigInteger total = BigInteger.ZERO;
total = total.add(fiveThousand);
total = total.add(fiftyThousand);
total = total.add(fiveHundredThousand);
System.out.println(total);
}
Now it prints 555000
189
17-214/514
The moral
190
17-214/514
Breaking changes can be hard to avoid
191
17-214/514
Awareness is Distracting and Expensive
192
17-214/514
minimizing number of dependencies
193
preparations
once adopted
193
17-214/514
194
17-214/514
195
looking at dependency’s project characteristics
identifying abandonment
195
17-214/514
Dependency is abandoned. What now?
switch to alternative dependency
seek support from others
fork/vendor dependency
[try to] contribute to dependency
create workaround yourself
refactor code to minimize dependency use
help find new maintainers
196
196
17-214/514
197
17-214/514
Heartbleed Bug
198
17-214/514
“In this case, the software [...] was maintained by a single person for close to 20 years. And that single person was, as far as I know, not being paid for it, despite the software being used extremely widely.”� - Andres Freund, Microsoft
199
17-214/514
Ever looked at npm install output (again)?
200
17-214/514
201
17-214/514
Recommended reading: https://republicans-oversight.house.gov/wp-content/uploads/2018/12/Equifax-Report.pdf
202
17-214/514
203
17-214/514
Large Attack Surface
204
17-214/514
As a developer:�How to protect against malicious CI tools, vulnerabilities in IDE plugins, …?
205
17-214/514
Reproducible Builds
206
17-214/514
Principles of Software Construction: Objects, Design, and Concurrency��Designing and Testing for Robustness in�Large & Distributed Systems��Claire Le Goues and Jeremy Lacomis
207
17-214/514
Modern software is dominated by systems composed of [components, APIs, modules], developed by completely different people, communicating over a network!
208
17-214/514
Distributed systems
209
17-214/514
Retry!
const delay = retryCount => new Promise(resolve =>� setTimeout(resolve, 10 ** retryCount));
const getResource = async (retryCount = 0, lastError = null) => {
if (retryCount > 5) throw new Error(lastError);
try {
return apiCall();
} catch (e) {
await delay(retryCount);
return getResource(retryCount + 1, e);
}
};
210
17-214/514
What’s the difference between the �Proxy and the Adapter Design Patterns?
211
17-214/514
Principle: Modular Protection
212
17-214/514
Ensuring Idempotence
213
17-214/514
Java Platform Module System
Since Java 9 (2017); built-in alternative to OSGi
Modularized JDK libraries itself
Several technical differences to OSGi (e.g., visibility vs access protection, handling of diamond problem)
module A {
exports org.example.foo;
exports org.example.bar;
}
module B {
require A;
}
214
17-214/514
The Module Pattern
<html>
<header>
<script type="text/javascript" src="lib1.js"></script>
<script type="text/javascript">
const m1 = (function () {
const export = {}
const x = 1;
export.x = x;
return export;
}());
</script>
<script type="text/javascript" src="lib2.js"></script>
...
215
17-214/514
The Diamond Problem
What now?
D
A
B
C
v1.4.1
v0.1.2
v2.7.3
v2.7.5
216
17-214/514
Fallacies of Distributed Computing by Peter Deutsch
217
17-214/514
Recall: Test Doubles
Concern that the third-party API might fail is not the only reason to use test doubles
218
17-214/514
Fault injection
Code
Mock Facebook
Test driver
class FacebookSlowStub implements FacebookAPI {
void connect() {}
List<Node> getFriends(String name) {
Thread.sleep(4000);
if (name.equals("john")) {
return List.of(...);
} // ...
}
}
219
17-214/514
What Can Go Wrong?
Let’s say Service A is your framework, B is a plugin
220
17-214/514
Chaos Engineering
Experimenting on a distributed system in order to build confidence in the system’s capability to withstand turbulent conditions in production
221
17-214/514
Principles of Software Construction�
DevOps��Claire Le Goues, Jeremy Lacomis
222
17-214/514
So you want to build a cathedral
https://i.etsystatic.com/6245917/r/il/7cb2e4/870606290/il_fullxfull.870606290_mab1.jpg
223
17-214/514
So you want to build a cathedral large system
224
17-214/514
Release cycle of Facebook’s apps
225
17-214/514
226
17-214/514
Early days: Boxed software, infrequent releases
227
17-214/514
Another example Facebook release cycle
228
17-214/514
229
17-214/514
230
17-214/514
Principle: Automation everywhere
231
17-214/514
232
17-214/514
Virtual Machines offer Machines as Code
Multiple VMs can sit on one server
VMs provide complete isolation
But, “translation” from guest OS to�host is slow, clunky
And each VM has entire OS, filesys
https://www.docker.com/resources/what-container/
233
17-214/514
CC BY-SA 4.0 Khtan66
234
17-214/514
235
17-214/514
Testing in �Production
236
17-214/514
237
237
17-214/514
Canary�Releases
238
17-214/514
Real DevOps Pipelines are �Complex
239
Chunqiang Tang, Thawan Kooburat, Pradeep Venkatachalam, Akshay Chander, Zhe Wen, Aravind Narayanan, Patrick Dowell, and Robert Karl. Holistic Configuration Management at Facebook. Proc. of SOSP: 328--343 (2015).
239
17-214/514
Quick/genuine survey!
https://canvas.cmu.edu/courses/40772/quizzes/133958
240
17-214/514
Towards People and Process
241
17-214/514
What is software engineering?
242
17-214/514
Compare to other forms of engineering
243
17-214/514
1968 NATO Conference on Software Engineering
244
17-214/514
Compare to other forms of engineering
245
17-214/514
Sociotechnical systems
246
17-214/514
247
17-214/514
248
17-214/514
Major topics in 17-313 (Foundations of SE)
249
17-214/514
Q: How does a software project become a year late?
250
17-214/514
The “almost done” problem
time
% completed
reported
progress
planned actual
251
17-214/514
Brooks’s law
252
17-214/514
Large teams (29 people) create around six times as many defects as small teams (three people) and obviously burn through a lot more money. Yet the large team appears to produce about the same amount of output in only an average of 12 days less time. This is a truly astonishing finding, though it fits with my personal experience on projects over 35 years.
- Phillip Amour, 2006, CACM 49:9
253
17-214/514
Software development process
254
17-214/514
Wishful thinking
Effort
Time
Project
start
Project
end
100%
0%
Trashing / rework
Productive coding
255
17-214/514
Developers perceive process as overhead, waste
Effort
Time
Project
start
Project
end
100%
0%
Trashing / rework
Productive coding
Process: Cost and time estimates, writing requirements, design,
change management, quality assurance plan,
development and integration plan, …
256
17-214/514
The reality, without enough process
Trashing / rework
Effort
Time
Project
start
Project
end
100%
0%
Productive coding
Process
257
17-214/514
A better ideal
Effort
Time
Project
start
Project
end
100%
0%
Productive coding
Process
Trashing / rework
258
17-214/514
Testimonials from the real world
“We had initially scheduled time to write tests for both front and back end systems, although this never happened.”
259
17-214/514
Testimonials from the real world
“We had initially scheduled time to write tests for both front and back end systems, although this never happened.”
“Due to the lack of time, we could only conduct individual pages’ unit testing. Limited testing was done using use cases. Our team felt that this testing process was rushed and more time and effort should be allocated.”
260
17-214/514
How do you get developers to write software tests?
261
17-214/514
How do you get developers to write software tests?
Better: How do you get developers to write the right amount of tests?
262
17-214/514
One approach: Test driven development (TDD)
263
17-214/514
Another approach: Test automation via CI
264
17-214/514
What next?
265
17-214/514
Summary
266
17-214/514